/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.remoting.transport.async;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.HashMap;

import EDU.oswego.cs.dl.util.concurrent.Executor;
import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import EDU.oswego.cs.dl.util.concurrent.ThreadFactory;
import org.jboss.logging.Logger;
import org.jboss.remoting.InvokerLocator;
import org.jboss.remoting.transport.async.bio.BlockingChannel;
import org.jboss.remoting.transport.async.bio.BlockingServer;

/**
 * An application wide registry to hold objects that
 * must be shared accross application components. 
 * 
 * @author <a href="mailto:hiram@coredevelopers.net">Hiram Chirino</a>
 */
public class Registry {
	
	static private final Logger log = Logger.getLogger(Registry.class);
	
	static private ChannelServer dynamicServer;
	static private ChannelServer defaultServer;
	static private PooledExecutor workManager;
	static private HashMap channelPools = new HashMap();
	
	static public boolean MOCK_APPLET_SECURITY=false;
	
	/**
	 * Used as the key into the channelPools Map.
	 * 
	 * The ipaddress and port are the only needed fields
	 * to locate a channel pool. 
	 */
	private static class URIKey {
		
		String ipaddress;
		int port;
		
		URIKey(InvokerLocator uri) throws UnknownHostException {
			ipaddress = uri.getHost();
			port = uri.getPort();
		}
		
		/*
		 * The key is used in a homogenous map.  We can cheat. 
		 */
		public boolean equals(Object obj) {
			return
				((URIKey)obj).port == port && 
				((URIKey)obj).ipaddress.equals(ipaddress);
		}
		public int hashCode() {
			return ipaddress.hashCode()+port;
		}
	}
	
	/**
	 * Factory method to create AsynchChannel instances.
	 * TODO: when the NBIO AsynchChannel is available, this needs
	 * a way to choose which implementation is used. (jdk and/or system prop)? 
	 */
	public static Channel createAsynchChannel() {
		return new BlockingChannel();
	}
		
	/**
	 * Factory method to create AsynchChannelServer instances.
	 * TODO: when the NBIO AsynchChannelServer is available, this needs
	 * a way to choose which implementation is used. (jdk and/or system prop)? 
	 */
	public static ChannelServer createAsynchChannelServer() {
		return new BlockingServer();
	}	

	private static int nextWorkerID=0;
	private static int getNextWorkerID() {
		return nextWorkerID++;
	}
	/**
	 * Provides a thread pool that can be shared accros components.
	 */
	synchronized public static Executor getWorkManager() {
		if( workManager!=null )
			return workManager;
		
		PooledExecutor p = new PooledExecutor();
		p.setKeepAliveTime(2000);
		p.setMinimumPoolSize(5);
		p.setMaximumPoolSize(Integer.MAX_VALUE);
		p.setThreadFactory(new ThreadFactory(){
         public Thread newThread(Runnable arg0)
         {
         	return new Thread(arg0, "Remoting 'async' protocol worker "+Registry.getNextWorkerID());
         }});
		
		workManager=p; 
		return workManager;
	}

	/**
	 * Gets the system wide AsynchChannelServer.  If a AsynchChannelServer
	 * has not been registed explicitly (when running in the jboss server),
	 * It attempts to create an AsynchChannelServer that listens on an 
	 * annonymous port.  Returns null if the AsynchChannelServer cannot be
	 * created. 
	 */
	synchronized public static ChannelServer getDefaultServer() {
		if( defaultServer!=null )
			return defaultServer;
		if( dynamicServer!=null )
			return dynamicServer;
			
		if( MOCK_APPLET_SECURITY )
			return null;
		
		// This jvm did not have a server running.  try to start the
		// server on a dynamic port.
		try {
			dynamicServer = createAsynchChannelServer();
			dynamicServer.bind(new InvokerLocator("async://0.0.0.0:0"));
			dynamicServer.start();
			return dynamicServer;
		} catch (IOException e) {
			// This could happen if a the security constraints don't
			// allow us to bind to a port.
			return null;
		}
	}

	/**
	 * Sets the application wide server.  This gets called when running
	 * in the JBoss sever and the server is explicity configured.
	 * 
	 * @param server
	 */
	synchronized public static void setDefaultServer(ChannelServer server) {
		defaultServer = server;		
	}

	/**
	 * Keeps a map of uri->ChannelPool objects.  Creates the 
	 * ChannelPool if it the first time you access the uri.
	 * 
	 * TODO: think of way to remove ChannelPool objects that are not being used.
	 * 
	 * @param server
	 */
	static public synchronized ChannelPool getChannelPool( InvokerLocator uri ) throws UnknownHostException {
		
		URIKey key = new URIKey(uri);
		ChannelPool rc = (ChannelPool) channelPools.get(key);
		if( rc == null ) {
			rc = new ChannelPool(uri);
			channelPools.put(key, rc);
		}
		return rc;
	}

	static private AsyncServerInvoker serverInvoker;
	public static void setServerInvoker(AsyncServerInvoker invoker) {
		serverInvoker = invoker;
	}

	public static AsyncServerInvoker getServerInvoker() {
		return serverInvoker;
	}

}
