/*
 * @(#)POAFactory.java	1.23 07/03/13
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.oa.poa ;

import java.util.Set ;
import java.util.HashSet ;
import java.util.Collections ;
import java.util.Iterator ;
import java.util.Map ;
import java.util.WeakHashMap ;

import org.omg.CORBA.OBJECT_NOT_EXIST ;
import org.omg.CORBA.TRANSIENT ;

import org.omg.CORBA.ORBPackage.InvalidName ;

import org.omg.PortableServer.Servant ;
import org.omg.PortableServer.POA ;
import org.omg.PortableServer.POAManager ;

import com.sun.corba.se.spi.oa.ObjectAdapter ;
import com.sun.corba.se.spi.oa.ObjectAdapterFactory ;

import com.sun.corba.se.spi.ior.ObjectAdapterId ;

import com.sun.corba.se.spi.orb.ORB ;

import com.sun.corba.se.spi.orbutil.closure.Closure ;
import com.sun.corba.se.spi.orbutil.closure.ClosureFactory ;

import com.sun.corba.se.spi.protocol.PIHandler ;

import com.sun.corba.se.spi.logging.CORBALogDomains ;

import com.sun.corba.se.impl.logging.POASystemException ;
import com.sun.corba.se.impl.logging.OMGSystemException ;

import com.sun.corba.se.impl.orbutil.ORBConstants ;

import com.sun.corba.se.impl.oa.poa.POAManagerImpl ;

public class POAFactory implements ObjectAdapterFactory 
{
    // Maps servants to POAs for deactivating servants when unexportObject is called.
    // Maintained by POAs activate_object and deactivate_object.
    private Map exportedServantsToPOA = new WeakHashMap();

    private Set poaManagers ;
    private int poaManagerId ;
    private int poaId ;
    private POAImpl rootPOA ;
    private DelegateImpl delegateImpl;
    private ORB orb ;
    private POASystemException wrapper ;
    private OMGSystemException omgWrapper ;
    private boolean isShuttingDown = false;

    public POASystemException getWrapper() 
    {
	return wrapper ;
    }

    /** All object adapter factories must have a no-arg constructor.
    */
    public POAFactory() 
    {
	poaManagers = Collections.synchronizedSet(new HashSet(4));
	poaManagerId = 0 ;
	poaId = 0 ;
	rootPOA = null ;
	delegateImpl = null ;
	orb = null ;
    }

    public synchronized POA lookupPOA (Servant servant) 
    {
        return (POA)exportedServantsToPOA.get(servant);
    }

    public synchronized void registerPOAForServant(POA poa, Servant servant) 
    {
        exportedServantsToPOA.put(servant, poa);
    }

    public synchronized void unregisterPOAForServant(POA poa, Servant servant) 
    {
        exportedServantsToPOA.remove(servant);
    }

// Implementation of ObjectAdapterFactory interface

    public void init( ORB orb ) 
    {
	this.orb = orb ;
	wrapper = POASystemException.get( orb,
	    CORBALogDomains.OA_LIFECYCLE ) ;
	omgWrapper = OMGSystemException.get( orb,
	    CORBALogDomains.OA_LIFECYCLE ) ;
	delegateImpl = new DelegateImpl( orb, this ) ;
	registerRootPOA() ;

	POACurrent poaCurrent = new POACurrent(orb);
	orb.getLocalResolver().register( ORBConstants.POA_CURRENT_NAME, 
	    ClosureFactory.makeConstant( poaCurrent ) ) ;
    }

    public ObjectAdapter find( ObjectAdapterId oaid )
    {
	POA poa=null;
	try {
	    boolean first = true ;
	    Iterator iter = oaid.iterator() ;
	    poa = getRootPOA();
	    while (iter.hasNext()) {
		String name = (String)(iter.next()) ;

		if (first) {
		    if (!name.equals( ORBConstants.ROOT_POA_NAME ))
			throw wrapper.makeFactoryNotPoa( name ) ;
		    first = false ;
		} else {
		    poa = poa.find_POA( name, true ) ;
		}
	    }
	} catch ( org.omg.PortableServer.POAPackage.AdapterNonExistent ex ){
	    throw omgWrapper.noObjectAdaptor( ex ) ;
	} catch ( OBJECT_NOT_EXIST ex ) {
	    throw ex;
	} catch ( TRANSIENT ex ) {
	    throw ex;
	} catch ( Exception ex ) {
	    throw wrapper.poaLookupError( ex ) ;
	}

	if ( poa == null )
	    throw wrapper.poaLookupError() ;

	return (ObjectAdapter)poa;
    }

    public void shutdown( boolean waitForCompletion )
    {
    	// It is important to copy the list of POAManagers first because 
	// pm.deactivate removes itself from poaManagers!
	Iterator managers = null ;
	synchronized (this) {
	    isShuttingDown = true ;
	    managers = (new HashSet(poaManagers)).iterator();
	}

	while ( managers.hasNext() ) {
	    try {
	        ((POAManager)managers.next()).deactivate(true, waitForCompletion);
	    } catch ( org.omg.PortableServer.POAManagerPackage.AdapterInactive e ) {}
	}
    }

// Special methods used to manipulate global POA related state

    public synchronized void removePoaManager( POAManager manager ) 
    {
        poaManagers.remove(manager);
    }

    public synchronized void addPoaManager( POAManager manager ) 
    {
        poaManagers.add(manager);
    }

    synchronized public int newPOAManagerId()
    {
	return poaManagerId++ ;
    }

    public void registerRootPOA()
    {
	// We delay the evaluation of makeRootPOA until
	// a call to resolve_initial_references( "RootPOA" ).
	// The Future guarantees that makeRootPOA is only called once.
	Closure rpClosure = new Closure() {
	    public Object evaluate() {
		return POAImpl.makeRootPOA( orb ) ;
	    }
	} ;

	orb.getLocalResolver().register( ORBConstants.ROOT_POA_NAME, 
	    ClosureFactory.makeFuture( rpClosure ) ) ;
    }


    public synchronized POA getRootPOA()
    {
	if (rootPOA == null) {
	    // See if we are trying to getRootPOA while shutting down the ORB.
	    if (isShuttingDown) {
		throw omgWrapper.noObjectAdaptor( ) ;
	    }

	    try {
		Object obj = orb.resolve_initial_references(
		    ORBConstants.ROOT_POA_NAME ) ;
		rootPOA = (POAImpl)obj ;
	    } catch (InvalidName inv) {
		throw wrapper.cantResolveRootPoa( inv ) ;
	    } 
	}

	return rootPOA;
    }

    public org.omg.PortableServer.portable.Delegate getDelegateImpl() 
    {
	return delegateImpl ;
    }

    synchronized public int newPOAId()
    {
	return poaId++ ;
    }

    public ORB getORB() 
    {
	return orb ;
    }
} 
