/*
 *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 *  Copyright 2000-2009 Sun Microsystems, Inc. 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.html
 *  or mq/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 mq/legal/LICENSE.txt.  Sun designates
 *  this particular file as subject to the "Classpath" exception as provided by
 *  Sun in the GPL Version 2 section of the License file that accompanied this
 *  code.  If applicable, add the following below the License Header, with the
 *  fields enclosed by brackets [] replaced by your own identifying information:
 *  "Portions Copyrighted [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. 
 *
 *  EmbeddedBrokerRunner.java
 *
 *  @(#)EmbeddedBrokerRunner.java	1.20 07/12/07
 */

package com.sun.messaging.jms.ra;

import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.sun.messaging.jmq.jmsclient.runtime.BrokerInstance;
import com.sun.messaging.jmq.jmsclient.runtime.ClientRuntime;
import com.sun.messaging.jmq.jmsservice.BrokerEvent;
import com.sun.messaging.jmq.jmsservice.BrokerEventListener;
import com.sun.messaging.jmq.jmsservice.JMSService;

/**
 *  Runs an embedded broker instance through
 *  the Broker* interfaces exposed for in-process
 *  broker lifecycle control
 */

public class EmbeddedBrokerRunner
        implements BrokerEventListener {

    /** Properties to be used by the embedded broker */
    private Properties brokerProps = null;

    /** The in-VM broker instance  */
    private BrokerInstance directBroker;

    /** The JMSService acquired from the JMSBroker (used only if the RA is using RADirect) */
    private JMSService jmsservice= null;

    private String brokerType;

    /* Loggers */
    private static transient final String _className =
            "com.sun.messaging.jms.ra.EmbeddedBrokerRunner";
    protected static transient final String _lgrNameLifecycle =
            "javax.resourceadapter.mqjmsra.lifecycle";
    protected static transient final Logger _loggerL =
            Logger.getLogger(_lgrNameLifecycle);
    protected static transient final String _lgrMIDPrefix = "MQJMSRA_EB";
    protected static transient final String _lgrMID_EET = _lgrMIDPrefix + "1001: ";
    protected static transient final String _lgrMID_INF = _lgrMIDPrefix + "1101: ";
    protected static transient final String _lgrMID_WRN = _lgrMIDPrefix + "2001: ";
    protected static transient final String _lgrMID_ERR = _lgrMIDPrefix + "3001: ";
    protected static transient final String _lgrMID_EXC = _lgrMIDPrefix + "4001: ";

    public EmbeddedBrokerRunner(
            String brokerTypeArg, String brokerInstanceName, String brokerBindAddress, int brokerPort,
            String brokerHomeDir, String brokerLibDir, String brokerVarDir, String brokerJavaDir,
            String brokerExtraArgs,
            boolean useJNDIRMIServiceURL, int rmiRegistryPort, boolean startRMIRegistry,
            boolean useSSLJMXConnector, String adminUsername, String adminPassword, String adminPassFile, boolean doBind, Properties sysProps)
    {
    	
    	if (_loggerL.isLoggable(Level.FINER)){
    		// log input parameters
	        Object params[] = new Object[16];
	        params[0] = brokerInstanceName;
	        params[1] = brokerBindAddress;
	        params[2] = Integer.toString(brokerPort);
	        params[3] = brokerHomeDir;
	        params[4] = brokerLibDir;
	        params[5] = brokerVarDir;
	        params[6] = brokerJavaDir;
	        params[7] = brokerExtraArgs;
	        params[8] = new Boolean(useJNDIRMIServiceURL);
	        params[9] = new Integer(rmiRegistryPort);
	        params[10] = new Boolean(startRMIRegistry);
	        params[11] = new Boolean(useSSLJMXConnector);
	        params[12] = adminUsername;
	        params[13] = adminPassFile;
	        params[14] = doBind;
	        params[15] = sysProps;
	        _loggerL.entering(_className, "constructor()", params);
    	}

        brokerType = brokerTypeArg;

        // assemble broker arguments from method arguments
       	String[] brokerArgs = assembleBrokerArgs(brokerInstanceName,
				brokerPort, brokerHomeDir, brokerLibDir, brokerVarDir,
				brokerExtraArgs, useJNDIRMIServiceURL, rmiRegistryPort,
				startRMIRegistry, doBind);
    	
       	// instantiate the broker instance
        createTheInVMBrokerInstance();
		
		// parse the broker args into a properties object that will be passed to the broker
        // (need to do this after we instantiate the broker instance)
		brokerProps=parseArgs(brokerArgs);

        if (brokerBindAddress != null && !("localhost".equals(brokerBindAddress)) ) {
            brokerProps.setProperty("imq.hostname", brokerBindAddress);
        }

        // add the supplied properties to the properties object that will be passed to the broker
        for (Enumeration e = sysProps.keys() ; e.hasMoreElements() ;) {
            String key = (String)e.nextElement();
            if (key != null) {
                String val = (String)sysProps.getProperty(key);
                brokerProps.setProperty(key, val);
            }
        }

    }

    /**
     * Assemble a String[] of broker arguments corresponding to the supplied method arguments
     * 
     * @param brokerInstanceName
     * @param brokerPort
     * @param brokerHomeDir
     * @param brokerLibDir
     * @param brokerVarDir
     * @param brokerExtraArgs
     * @param useJNDIRMIServiceURL
     * @param rmiRegistryPort
     * @param startRMIRegistry
     * @return The String[] of broker arguments
     */
	private String[] assembleBrokerArgs(String brokerInstanceName,
			int brokerPort, String brokerHomeDir, String brokerLibDir,
			String brokerVarDir, String brokerExtraArgs,
			boolean useJNDIRMIServiceURL, int rmiRegistryPort,
			boolean startRMIRegistry, boolean doBind) {
    	
        Vector<String> v = new Vector<String>();

        //Add extra args first; explicit config will override args 
        if (brokerExtraArgs != null && !("".equals(brokerExtraArgs)) ) {
            StringTokenizer st = new StringTokenizer(brokerExtraArgs, " ");
            while (st.hasMoreTokens()) {
                String t = st.nextToken();
                v.add(t);
            }
        }

        //Add explicit config
        //
        v.add("-port");
        v.add(Integer.toString(brokerPort));

        if (brokerInstanceName != null) {
            v.add("-name");
            v.add(brokerInstanceName);
        }
        
        if (!doBind) {
            v.add("-nobind");
        }
 
        if (brokerHomeDir != null) {
            v.add("-imqhome");
            v.add(brokerHomeDir);
        }
 
        if (brokerVarDir != null) {
            v.add("-varhome");
            v.add(brokerVarDir);
        }

        if (brokerLibDir != null && !("".equals(brokerLibDir))) {
            v.add("-libhome");
            v.add(brokerLibDir);
        }

        if (useJNDIRMIServiceURL == true) {
            if (startRMIRegistry == true) {
                v.add("-startRmiRegistry");
            } else {
                v.add("-useRmiRegistry");
            }
            v.add("-rmiRegistryPort");
            v.add(Integer.toString(rmiRegistryPort));
        }
        
        //Add -save cli option to have the broker save properties at startup
        //The properties that are to be saved are defined by the broker
        //Enables MQ cli utilities that depend on the configuration to be run
        //after AS has shut down.
        v.add("-save");

        //Add -silent cli option to minimize broker output to AS log
        v.add("-silent");

    	String[] brokerArgs = (String []) v.toArray(new String[0]);
		return brokerArgs;
	}

    /**
     * Create the in-JVM broker instance
     * 
     * Requires field: brokerType
     * Sets fields: directBroker  
     */
	private void createTheInVMBrokerInstance() {

		// get a client runtime object
		ClientRuntime runtime = ClientRuntime.getRuntime();

		// get a direct broker instance
		try {
			directBroker = runtime.createBrokerInstance();
		} catch (ClassNotFoundException e) {
			System.out.println("SJSMQRA_EB:DebugCFN-ExcMsg=" + e.getMessage());
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.out.println("SJSMQRA_EB:DebugCFN-ExcMsg=" + e.getMessage());
			e.printStackTrace();
		} catch (InstantiationException e) {
			System.out.println("SJSMQRA_EB:DebugCFN-ExcMsg=" + e.getMessage());
			e.printStackTrace();
		}
	}		
    
	/**
	 * Parse the supplied broker arguments and convert them into a Properties object
	 * 
	 * Requires fields: brokerType, apiDirectBroker or raDirectBroker 
	 * 
	 * @param brokerArgs the supplied broker arguments
	 * @return a Properties object corresponding to the supplied arguments
	 */
    private Properties parseArgs(String[] brokerArgs){
    	
    	return directBroker.parseArgs(brokerArgs);

    }

    public synchronized void
    init()
    {
        _loggerL.entering(_className, "init()");
        
       	directBroker.init(brokerProps, this);

    }

    public synchronized void start() {
		_loggerL.entering(_className, "start()");
		_loggerL.config(_lgrMID_INF + "EB-start:brokerProps=" + brokerProps.toString());

		directBroker.start();
	}

    protected synchronized void stop() {
		_loggerL.entering(_className, "stop()");
		try {
			directBroker.stop();
			directBroker.shutdown();
			directBroker = null;
		} catch (Exception bse) {
			_loggerL.severe(_lgrMID_EXC + "stop:Exception on in-VM broker shutdown:msg=" + bse.getMessage());
			bse.printStackTrace();
		}
	}

    public JMSService getJMSService(){
    	
    	if (brokerType!=ResourceAdapter.BROKER_TYPE_RADIRECT) {
    		return null;
    	}
    	
        if (jmsservice == null) {
        		jmsservice = directBroker.getJMSService();
        }
        return jmsservice;
    }
    
    //BrokerEventListener methods
    public void brokerEvent(BrokerEvent bErrEvt){
        _loggerL.fine(_lgrMID_INF+"stateChanged:" + bErrEvt);
        if (bErrEvt.getType()==BrokerEvent.Type.RESTART){
        	//System.out.println("Restarting embedded broker");
        	stop();
            // create the in-JVM broker instance
        	createTheInVMBrokerInstance();

        	init();
        	start();
        }
    }
    
    /**
     *  Notify the BrokerEventLstener that the broker would like to exit.<p>
     *
     *  @param event The BrokerEvent that contains information about the reason
     *               why the broker is requesting to exit. The event Id could
     *               be one of:<p>
     *       <UL>
     *         <LI>REASON_SHUTDOWN</LI>
     *         <LI>REASON_RESTART</LI>
     *         <LI>REASON_FATAL</LI>
     *         <LI>REASON_ERROR</LI>
     *         <LI>REASON_EXCEPTION</LI>
     *         <LI>REASON_STOP</LI>
     *       </UL>
     *  @param thr An optional Throwable
     *
     *  @return true if the broker should clean up and exit and false
     *         if the request to exit was denied (if the request to
     *         shutdown after a fatal error is denied, the broker may not
     *         respond correctly).
     */
    public boolean exitRequested (BrokerEvent bEvt, Throwable thr){
    	
        System.out.println("SJSMQRA_EB:Debug-exitRequested:" + bEvt);
        
        // We would like to allow restart of an in-VM broker if clients are using APIDirect rather than RADirect
        // however this doesn't work yet so the following code is commented out
        // Note that if we ever do allow this, need to change RABrokerLifeCycleTest011 
        // and reinstate RABrokerLifeCycleTestRestartAPIDirect
        
        // allow restart only if the resource adapter is using APIDIRECT 
//    	if ((brokerType==ResourceAdapter.BROKER_TYPE_APIDIRECT)&&(bEvt.getType()==BrokerEvent.Type.RESTART)) {
//            System.out.println("SJSMQRA_EB:Debug-exitRequested[granted bcause this is a restart]:" + bEvt);
//    		return true;
//    	}
    	
        if (bEvt.getType() == BrokerEvent.Type.FATAL_ERROR) {
            // have to exit
            System.out.println("SJSMQRA_EB:Debug-exitRequested[granted because this is a fatal error]:" + bEvt);
            Thread.dumpStack();
            return true;
        }
        
        // deny the exit
        System.out.println("SJSMQRA_EB:Debug-exitRequestedd[denied because this is an embedded broker]:" + bEvt);
        return false;  
    }

}
