package org.jacorb.orb.giop;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2004 Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.jacorb.config.*;
import org.jacorb.orb.DefaultProfileSelector;
import org.jacorb.orb.ORB;
import org.jacorb.orb.ProfileSelector;
import org.jacorb.orb.diop.DIOPFactories;
import org.jacorb.orb.factory.SocketFactoryManager;
import org.jacorb.orb.giop.TransportListener.Event;
import org.jacorb.util.ObjectUtil;
import org.omg.CORBA.BAD_PARAM;
import org.omg.ETF.Factories;

/**
 * This class manages Transports. On the one hand it creates them, and
 * on the other it enforces an upper limit on the open transports.
 *
 * The class also receives notifications from threads that are about do use a 
 * Transport and notifies any interested listeners. "Use" is defined as 
 * sending (or handling) a request.
 *
 * @author Nicolas Noffke
 * @version $Id: TransportManager.java,v 1.30 2009-05-03 21:35:56 andre.spiegel Exp $
 * */

public class TransportManager
    implements Configurable
{
    /** the configuration object  */
    private org.jacorb.config.Configuration configuration = null;

    /** configuration properties */
    private Logger logger = null;
    private List factoryClassNames = null;
    private ProfileSelector profileSelector = null;
    private final SocketFactoryManager socketFactoryManager;

    /**
     * Maps ETF Profile tags (Integer) to ETF Factories objects.
     */
    private Map  factoriesMap  = null;

    /**
     * List of all installed ETF Factories.  This list contains an
     * instance of each Factories class, ordered in the same way as
     * they were specified in the jacorb.transport.factories property.
     */
    private List factoriesList = null;

    /**
     * The first listener (in a chain of instances), representing 
     * parties with interest in Transport events.
     */
    private TransportListener listener = null;

    public TransportManager( ORB orb )
    {
        socketFactoryManager = new SocketFactoryManager(orb);
    }

    public void configure(Configuration myConfiguration)
        throws ConfigurationException
    {
        this.configuration = (org.jacorb.config.Configuration)myConfiguration;
        logger =
            configuration.getLogger("jacorb.orb.giop");
        socketFactoryManager.configure(configuration);

        // get factory class names
        factoryClassNames =
            this.configuration.getAttributeList("jacorb.transport.factories");

        if (factoryClassNames.isEmpty())
        {
            factoryClassNames.add("org.jacorb.orb.iiop.IIOPFactories");
        }

        // get profile selector info
        profileSelector =
            (ProfileSelector)configuration.getAttributeAsObject("jacorb.transport.client.selector");

        if (profileSelector == null)
        {
            profileSelector = new DefaultProfileSelector();
        }
    }

    public ProfileSelector getProfileSelector()
    {
        return profileSelector;
    }

    public SocketFactoryManager getSocketFactoryManager()
    {
        return socketFactoryManager;
    }

    /**
     * Returns an ETF Factories object for the given tag, or null
     * if no Factories class has been defined for this tag.
     */
    public synchronized org.omg.ETF.Factories getFactories(int tag)
    {
        // This isn't ideal. If DIOPFactories was a full implementation then
        // this class should be added to the
        // TransportManager::loadFactories. This shortcut block (which is used
        // by ParsedIOR) and the static caching in DIOPFactories wouldn't be
        // needed.
        if (tag == DIOPFactories.TAG_DIOP_UDP)
        {
            return DIOPFactories.getDIOPFactory();
        }

        if (factoriesMap == null)
        {
            loadFactories();
        }
        return (Factories)factoriesMap.get (ObjectUtil.newInteger(tag));
    }

    /**
     * Returns a list of Factories for all configured transport plugins,
     * in the same order as they were specified in the
     * jacorb.transport.factories property.
     */
    public synchronized List getFactoriesList()
    {
        if (factoriesList == null)
        {
            loadFactories();
        }
        return Collections.unmodifiableList(factoriesList);
    }

    /**
     * Build the factoriesMap and factoriesList.
     */
    private void loadFactories()
    {
        if (configuration == null )
        {
            throw new org.omg.CORBA.BAD_INV_ORDER("TransportManager not configured!");
        }

        if (factoryClassNames == null )
        {
            throw new org.omg.CORBA.INTERNAL("factoryClassNames may not be null");
        }

        factoriesMap  = new HashMap();
        factoriesList = new ArrayList();

        for (Iterator i = factoryClassNames.iterator(); i.hasNext();)
        {
            String className = (String)i.next();
            Factories factories = instantiateFactories(className);
            factoriesMap.put(new Integer(factories.profile_tag()), factories); // NOPMD
            factoriesList.add (factories);
        }
    }

    /**
     * Instantiates the given Factories class.
     */
    private org.omg.ETF.Factories instantiateFactories (String className)
    {
        try
        {
            // ObjectUtil.classForName() uses the context class loader.
            // This is important here because JacORB might be on the
            // bootclasspath, and the external transport on the normal
            // classpath.
            Class clazz = ObjectUtil.classForName(className);
            Object instance = clazz.newInstance();

            if (instance instanceof Configurable)
            {
                Configurable configurable = (Configurable)instance;
                configurable.configure(configuration);
            }

            logger.debug("created org.omg.ETF.Factories: " + className);

            return (Factories)instance;
        }
        catch (Exception e)
        {
            throw new BAD_PARAM
                ("could not instantiate Factories class " + className
                 + ", exception: " + e);
        }
    }

	public void notifyTransportListeners(GIOPConnection giopc) {

        if (listener != null) {
            listener.transportSelected (new Event (giopc));
        }
    }

    public void addTransportListener(TransportListener tl) {

        if (logger.isInfoEnabled ()) {
            logger.info ("Transport listener to add: " + tl);
        }

        if (tl != null) {
            addTransportListenerImpl (tl);
        }
    }

    private synchronized void addTransportListenerImpl(final TransportListener tl) {

        if (listener == null) {
            listener = tl;
        }
        else {

            listener = new TransportListener () {

                private final TransportListener next_ = listener;

                public void transportSelected(Event event) {

                    try {
                        tl.transportSelected (event);
                    }
                    finally {
                        next_.transportSelected (event);
                    }
                }
            };
        }
    }
 
}
