/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 * 
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * @(#)ConnectorServerInfo.java	1.10 06/28/07
 */ 

package com.sun.messaging.jmq.jmsserver.management.agent;

import java.util.List;
import java.util.Set;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Collection;
import java.util.Vector;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;

import com.sun.messaging.jmq.util.log.Logger;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.jmsserver.config.BrokerConfig;
import com.sun.messaging.jmq.jmsserver.util.BrokerException;
import com.sun.messaging.jmq.jmsserver.resources.BrokerResources;
import com.sun.messaging.jmq.io.MQAddress;

import com.sun.messaging.jmq.management.MQRMIClientSocketFactory;

/**
 * Class that represents one JMX connector server.
 *
 */
public class ConnectorServerInfo  {
    private static String	CONNECTOR_PROPBASE
				    		= Globals.IMQ + ".jmx.connector.";
    private static String	PROTOCOL_SUFFIX	= ".protocol";
    private static String	PORT_SUFFIX	= ".port";
    private static String	URLPATH_SUFFIX	= ".urlpath";
    private static String	URL_SUFFIX	= ".url";
    private static String	SSL_SUFFIX	= ".useSSL";
    private static String	BROKER_HOST_TRUSTED_SUFFIX 
						= ".brokerHostTrusted";
    private static String	BACKLOG_SUFFIX	= ".backlog";

    Agent		agent;
    JMXConnectorServer	connectorServer = null;
    JMXServiceURL	configuredURL = null;
    String		name = null;
    boolean		configuredActive = false,
			stopped = false;
    NotificationListener	listener;
    private BrokerResources			rb = Globals.getBrokerResources();

    public ConnectorServerInfo(Agent agent, String name, boolean configuredActive, 
				NotificationListener listener) 
					throws BrokerException  {
	this.name = name;
	this.configuredActive = configuredActive;
	this.listener = listener;
	this.agent = agent;

        initURL();
    }

    /*
    public ConnectorServerInfo(String name, 
			JMXConnectorServer connectorServer, 
				JMXServiceURL configuredURL)  {
	this.name = name;
	this.connectorServer = connectorServer;
	this.configuredURL = configuredURL;
    }
    */

    public JMXServiceURL getConfiguredJMXServiceURL()  {
        return (configuredURL);
    }

    public JMXServiceURL getJMXServiceURL()  {
	if (isActive())  {
	    return (connectorServer.getAddress());
	} else  {
	    return (configuredURL);
	}
    }

    public String getName()  {
	return (name);
    }

    public boolean isActive()  {
	if (!configuredActive)  {
	    return (false);
	}

	if (connectorServer == null)  {
	    return (false);
	}

	return (connectorServer.isActive());
    }

    public void start() throws IOException, BrokerException {
	if (!configuredActive)  {
	    throw new BrokerException(rb.getString(rb.W_JMX_CANNOT_START_INACTIVE_CONNECTOR, 
					name));
	}

	initConnectorServer();

        connectorServer.start();

	/*
	System.err.println(">>>>Starting connector at URL: " + getJMXServiceURL());
	System.err.println(">>>>\tURL path: " + getJMXServiceURL().getURLPath());
	System.err.println(">>>>Connector Server: " + connectorServer);
	*/
    }

    public void stop() throws IOException {
        if (!isActive())  {
	    return;
	}

        connectorServer.stop();
	stopped = true;

	/*
	 * Javadoc for JMXConnectorServer says that "A connector server 
	 * once stopped cannot be restarted."
	 */
        connectorServer = null;
	configuredURL = null;
    }

    public boolean isStopped()  {
	return (stopped);
    }

    public int getPort()  {
	return (getJMXServiceURL().getPort());
    }

    public String getProtocol()  {
	return (getJMXServiceURL().getProtocol());
    }

    public JMXConnectorServer getConnectorServer()  {
	return (connectorServer);
    }

    /*
     * Initialize the JMXServiceURL associated with this connector.
     * This class was initialized with just the connector 'name'
     * which has nothing to do with JMX, but merely a reference
     * into the broker configuration properties.
     *
     * There are various properties that can be used to configure
     * or construct the URL - the properties map more or less
     * to the constructor of the JMXServiceURL class:
     *
     *  public JMXServiceURL(String protocol, String host, int port, String urlPath)
     *
     * So, for example, the default JMX connector is named "jmxrmi"
     * and the properties that can be used to configure it's URL are:
     *
     *	imq.jmx.connector.<connector server name>.protocol
     *	imq.jmx.connector.<connector server name>.port
     *	imq.jmx.connector.<connector server name>.urlpath
     *	imq.jmx.connector.<connector server name>.url
     *
     * Out of the above, only the following is made public:
     *
     *	imq.jmx.connector.<connector server name>.urlpath
     *
     * because the only protocol supported in MQ 4.0 is rmi, and the port
     * is pretty much ignored for RMI connectors. No real reason url is 
     * not made public other than the fact that we want to simplify things
     * and it is not really needed.
     *
     * If urlpath is not specified via the above property, a default exists
     * and can be constructed with Agent.getDefaultJMXUrlPathBase().
     *
     * One point worth noting is that this fixed or JNDI form of the url
     * is only used when a RMI registry (or some form of JNDI repository)
     * is used to store the RMI stub for the connector.
     *
     * Example entry in configuration properties:
     *
     *	imq.jmx.connector.jmxrmi.urlpath=/foo/bar/connector1
     */
    private void initURL() throws BrokerException  {
	String		protocol = null,
			urlpath = null,
			jmxurl = null;
	int		port = 0;
	JMXServiceURL	url;
        BrokerConfig	config = Globals.getConfig();
        String          jmxHostname = Globals.getJMXHostname();

	if (configuredURL != null)  {
	    return;
	}

	jmxurl = config.getProperty(CONNECTOR_PROPBASE
			+ name + URL_SUFFIX);

	if (jmxurl == null)  {
	    protocol = config.getProperty(CONNECTOR_PROPBASE 
					+  name + PROTOCOL_SUFFIX, "rmi");
	    /*
	     * Get port value, defaulting to 0 if none was specified.
	     *
	     * The javadoc for JMXServiceURL says that getPort() will return
	     * 0 if none was specified.
	     */
	    port = config.getIntProperty(CONNECTOR_PROPBASE 
					+  name + PORT_SUFFIX, 0);
	    urlpath = config.getProperty(CONNECTOR_PROPBASE 
					+  name + URLPATH_SUFFIX);

	    if (urlpath == null)  {
		/*
		 * If imq.jmx.rmiregistry.start or imq.jmx.rmiregistry.use is set 
		 * to true, the default urlpath is:
	         *  /jndi/rmi://<brokerhost>:<rmiport>/<brokerhost>/<brokerport>/<connector name>
		 */
		if (agent.startRmiRegistry() || agent.useRmiRegistry())  {
		    urlpath = agent.getDefaultJMXUrlPathBase() + name;
		}
	    }
	}

	try  {
	    if (jmxurl != null)  {
	        url = new JMXServiceURL(jmxurl);
	    } else  {
                if (jmxHostname != null && 
                    !jmxHostname.equals(Globals.HOSTNAME_ALL)) {
	           url = new JMXServiceURL(protocol, jmxHostname, 
                                           port, urlpath);
                } else {
	           url = new JMXServiceURL(protocol, null, port, urlpath);
                }
	    }
	} catch (MalformedURLException mfe)  {
	    throw new BrokerException(rb.getString(rb.W_JMX_CREATE_URL_FOR_CONNECTOR_FAILED, 
					name), mfe);
	}

	configuredURL = url;
    }

    /*
     * Initialize the connector server. This method assumes the URL has been
     * initialized.
     *
     * The following properties can be used to further configure the
     * connector server:
     *	imq.jmx.connector.<connector server name>.useSSL
     *	imq.jmx.connector.<connector server name>.brokerHostTrusted
     *	imq.jmx.connector.<connector server name>.backlog
     * 
     */
    private void initConnectorServer() throws BrokerException  {
	JMXConnectorServer	cs;
        BrokerConfig		config = Globals.getConfig();
	MBeanServer		mbs = agent.getMBeanServer();
	HashMap			env = new HashMap();
	boolean			useSSL, brokerHostTrusted;
	String			jmxHostname = Globals.getJMXHostname();
	int			backlog;

	if (connectorServer != null)  {
	    return;
	}

	if (mbs == null)  {
	    throw new BrokerException(rb.getString(rb.X_JMX_CANT_CREATE_CONNECTOR_SVR, name));
	}

        useSSL = config.getBooleanProperty(CONNECTOR_PROPBASE
					+  name 
					+ SSL_SUFFIX, false);

	/*
	 * Query property that specifies if the broker is to be trusted.
	 * If this property is not set, by default the broker is *not*
	 * trusted.
	 */
        brokerHostTrusted = config.getBooleanProperty(CONNECTOR_PROPBASE
					+  name 
					+ BROKER_HOST_TRUSTED_SUFFIX, false);

	backlog = config.getIntProperty(CONNECTOR_PROPBASE 
					+  name 
					+ BACKLOG_SUFFIX, 0);

	try  {
	    /*
	     * We only support the RMI protocol in MQ 4.0
	     */
	    if (configuredURL.getProtocol().equals("rmi"))  {
		boolean useAuth = true;
		if (useAuth)  {
		    env.put(JMXConnectorServer.AUTHENTICATOR, new MQJMXAuthenticator(this));
		}

		/*
		 * Use MQ specific server socket factory only if:
		 *  - SSL is enabled
		 *  - need to specify a specific host/IP on a multihome system
		 */
	        if ((useSSL) || 
		     (jmxHostname != null && !jmxHostname.equals(Globals.HOSTNAME_ALL))) {
                    MQRMIServerSocketFactory ssf
				= new MQRMIServerSocketFactory(jmxHostname, backlog, useSSL);
                    env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
					ssf);
		}

		/*
		 * Use MQ specific client socket factory only if:
		 *  - need to specify a specific host/IP on a multihome system
		 */
	        if (jmxHostname != null && !jmxHostname.equals(Globals.HOSTNAME_ALL)) {
                    MQRMIClientSocketFactory csf
				= new MQRMIClientSocketFactory(jmxHostname,
						brokerHostTrusted, useSSL);
                    env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
					csf);
		} else  {
		    /*
		     * Use MQ specific client socket factory only if:
		     * - SSL is enabled and brokerHostTrusted is true
		     * If brokerHostTrusted is false, use the JDK provided
		     * javax.rmi.ssl.SslRMIClientSocketFactory
		     */
		    if (useSSL)  {
			if (brokerHostTrusted)  {
                            MQRMIClientSocketFactory csf
				= new MQRMIClientSocketFactory(jmxHostname,
						brokerHostTrusted, useSSL);
                            env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
					csf);
			} else  {
                            env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
					new javax.rmi.ssl.SslRMIClientSocketFactory());
			}
		    }
		}
	    }

            cs = JMXConnectorServerFactory.newJMXConnectorServer(configuredURL, env, mbs);
	} catch (Exception e)  {
	    throw new BrokerException(rb.getString(rb.W_JMX_ERROR_CREATING_CONNECTOR, 
					name), e);
	}

	cs.addNotificationListener(listener, null, this);

	connectorServer = cs;
    }
}

