/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (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/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.management.j2ee;

import java.util.Set;
import java.util.Map;

import javax.management.ObjectName;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.MBeanServer;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanException;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.ReflectionException;
import javax.management.MBeanServerInvocationHandler;

import com.sun.appserv.management.base.Util;
import com.sun.appserv.management.util.misc.GSetUtil;
import com.sun.appserv.management.util.jmx.JMXUtil;
import com.sun.appserv.management.util.misc.ExceptionUtil;
import com.sun.appserv.management.client.ProxyFactory;
import com.sun.appserv.management.j2ee.J2EEServer;
import com.sun.appserv.management.j2ee.J2EETypes;
import com.sun.appserv.management.j2ee.StateManageable;
import com.sun.appserv.management.j2ee.J2EEServer;

import com.sun.enterprise.management.support.Delegate;
import com.sun.enterprise.management.support.DummyDelegate;
import com.sun.enterprise.management.support.LoaderMBean;
import com.sun.enterprise.management.support.QueryMgrImpl;
import com.sun.enterprise.management.support.DelegateToMBeanDelegate;

import com.sun.enterprise.admin.mbeans.DomainStatusMBean;
import com.sun.enterprise.admin.mbeans.DomainStatus;
import com.sun.enterprise.admin.mbeans.DomainStatusHelper;
import com.sun.enterprise.ManagementObjectManager;

import com.sun.appserv.management.j2ee.J2EETypes;

/**
	JSR 77 extension representing an Appserver standalone server (non-clustered)

	Server MBean which will reside on DAS
	for enabling state management including start() and stop()
 */
public class DASJ2EEServerImpl
	extends J2EEServerImpl
	implements NotificationListener
{
	// vars
	private Delegate delegate;

		public
	DASJ2EEServerImpl()
	{
		super( J2EETypes.J2EE_SERVER, DummyDelegate.INSTANCE );
		delegate = DummyDelegate.INSTANCE;
	}

	static private final Class[]	DOMAIN_STATUS_INTERFACES	= 
		new Class[] { DomainStatusMBean.class };
	
		protected DomainStatusMBean
	getDomainStatus()
	{
		DomainStatusMBean	domainStatus	= null;
		try {
			final MBeanServer	mbeanServer = getMBeanServer();
			final Set<ObjectName>	candidates	= QueryMgrImpl.queryPatternObjectNameSet( 
				mbeanServer, JMXUtil.newObjectNamePattern( 
					"*", DomainStatusMBean.DOMAIN_STATUS_PROPS ) );
			final ObjectName on = GSetUtil.getSingleton( candidates );
			domainStatus = (DomainStatusMBean)MBeanServerInvocationHandler.
				newProxyInstance( mbeanServer, on, DomainStatusMBean.class, false );
		} catch (Exception e) {
			final Throwable rootCause = ExceptionUtil.getRootCause( e );
			getMBeanLogger().warning( rootCause.toString() + "\n" +
				ExceptionUtil.getStackTrace( rootCause ) );
		}
		return( domainStatus );
	}
	
		private boolean
	remoteServerIsRunning()
	{
		return (StateManageable.STATE_RUNNING == getstate());
	}

		private boolean
	remoteServerIsStartable()
	{
		int cState = getstate();

		if ((StateManageable.STATE_STOPPED == cState) ||
		    (StateManageable.STATE_FAILED == cState)) 
		{
			return true;
		} 
		else 
		{
			return false;
		}
	}
	
		private boolean
	remoteServerIsStoppable()
	{
		int cState = getstate();

		if ((StateManageable.STATE_STARTING == cState) ||
		    (StateManageable.STATE_RUNNING == cState) ||
		    (StateManageable.STATE_FAILED == cState)) 
		{
			return true;
		} 
		else 
		{
			return false;
		}
	}
	
		public void
	handleNotification( Notification notif , Object obj)
	{
		final String	notifType	= notif.getType();
		
		if ( notifType.equals( DomainStatusMBean.SERVER_STATUS_NOTIFICATION_TYPE ) )
		{
			final String	serverName = (String)
			    Util.getAMXNotificationValue( notif, DomainStatusMBean.SERVER_NAME_KEY );
			
			if ( serverName.equals( getServerName() ) )
			{
				setDelegate();
			}
		}
	}
	
		private void
	setDelegate()
	{
		if ( remoteServerIsRunning() )
		{
		    try {
			// get the object name for the old jsr77 server mBean
			com.sun.enterprise.ManagementObjectManager mgmtObjManager =
				com.sun.enterprise.Switch.getSwitch().getManagementObjectManager();

			String strON = mgmtObjManager.getServerBaseON(false, getServerName());
			
			
			final MBeanServerConnection remoteConn	=
				getDomainStatus().getServerMBeanServerConnection( getServerName() );
				
			ObjectName onPattern = new ObjectName(strON + ",*");

			final Set<ObjectName> names = JMXUtil.queryNames( remoteConn, onPattern, null);

			assert( names.size() == 1 );

			final ObjectName serverON = GSetUtil.getSingleton( names );

			final Delegate	delegate	=
				new DelegateToMBeanDelegate( remoteConn, serverON );
			
			setDelegate( delegate );
			setstartTime(System.currentTimeMillis());
		    } catch (Exception e) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( e );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
                    }
		}
		else
		{
			setDelegate( DummyDelegate.INSTANCE );
			setstartTime(0);
		}
	}
	
		public void
    preRegisterDone()
        throws Exception
	{
		super.preRegisterDone( );
		
		setstartTime( 0 );
		setDelegate();
	}
	
		protected String
	getServerName()
	{
		return( getSelfName() );
	}

		public boolean
	isstateManageable()
	{
		return true;
	}

 		public int
 	getstate()
 	{
		try {
			return (getDomainStatus().getstate(getServerName()));
		} catch (Exception e) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( e );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
                }
		return StateManageable.STATE_FAILED;
 	}

 		public void
 	start()
 	{
 		if ( remoteServerIsStartable() )
 		{
 			startRemoteServer();
 		}
 		else
 		{
			throw new RuntimeException("server is not in a startable state");
 		}
 	}
 	
 		public void
 	startRecursive()
 	{
 		start();
 	}
 	
 		public void
 	stop()
 	{
 		if ( remoteServerIsStoppable() )
 		{
 			stopRemoteServer();
 		}
 		else
 		{
			throw new RuntimeException("server is not in a stoppable state");
 		}
 	}


		public MBeanInfo
	getMBeanInfo()
	{

		MBeanInfo	superMBeanInfo	= super.getMBeanInfo();

		if (delegate != null) 
		{
			try {
				DummyDelegate dd = (DummyDelegate) delegate;
				final MBeanInfo	mbeanInfo	= new MBeanInfo(
					superMBeanInfo.getClassName(),
					superMBeanInfo.getDescription(),
					mergeAttributeInfos(superMBeanInfo.getAttributes(),
						getMBeanAttributeInfo()),
					superMBeanInfo.getConstructors(),
					mergeOperationInfos(superMBeanInfo.getOperations(),
						getMBeanOperationInfo()),
					superMBeanInfo.getNotifications());
				return( mbeanInfo );
			} catch (ClassCastException cce) {
				return superMBeanInfo;
			}
		} 
		else 
		{
			return superMBeanInfo;
		
		}

	}


	// attribute info

		private MBeanAttributeInfo[]
	mergeAttributeInfos(
		MBeanAttributeInfo[] infos1,
		MBeanAttributeInfo[] infos2 )
	{
		final MBeanAttributeInfo[] infos =
			new MBeanAttributeInfo[ infos1.length + infos2.length ];
		
		System.arraycopy( infos1, 0, infos, 0, infos1.length );
		System.arraycopy( infos2, 0, infos, infos1.length, infos2.length );
		
		return( infos );
	}


		private MBeanAttributeInfo[]
	getMBeanAttributeInfo()
	{
		MBeanAttributeInfo[] dAttributes = new MBeanAttributeInfo[1];
		dAttributes[0] = new MBeanAttributeInfo("state",
                                                "java.lang.Integer",
                                                "server state",
                                                true,
                                                false,
                                                false);
		return dAttributes;
	}


	// operation info

		private MBeanOperationInfo[]
	mergeOperationInfos(
		MBeanOperationInfo[] infos1,
		MBeanOperationInfo[] infos2 )
	{
		final MBeanOperationInfo[] infos =
			new MBeanOperationInfo[ infos1.length + infos2.length ];
		
		System.arraycopy( infos1, 0, infos, 0, infos1.length );
		System.arraycopy( infos2, 0, infos, infos1.length, infos2.length );
		
		return( infos );
	}


		private MBeanOperationInfo[]
	getMBeanOperationInfo()
	{
		MBeanOperationInfo[] dOperations = new MBeanOperationInfo[3];
		dOperations[0] = new MBeanOperationInfo("start",
                                                "start server instance",
                                                null,
                                                "void",
                                                MBeanOperationInfo.ACTION);
		dOperations[1] = new MBeanOperationInfo("stop",
                                                "stop server instance",
                                                null,
                                                "void",
                                                MBeanOperationInfo.ACTION);
		dOperations[2] = new MBeanOperationInfo("startRecursive",
                                                "start server instance",
                                                null,
                                                "void",
                                                MBeanOperationInfo.ACTION);
		return dOperations;
	}


		private void 
	startRemoteServer() 
	{

		try {
			// get the object name for servers config mBean
			ObjectName on = DomainStatusHelper.getServersConfigObjectName();

			// invoke start method on servers config mBean

			// get mBean server
			final MBeanServer server = getMBeanServer();

			// form the parameters
			Object[] params = new Object[1];
                	params[0] = getServerName();
                	String[] signature = {"java.lang.String"};

			// invoke the start method
                	server.invoke(on, "startServerInstance", params, signature);
		} catch (javax.management.MalformedObjectNameException mfone) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( mfone );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(mfone);
		} catch (javax.management.InstanceNotFoundException infe) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( infe );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(infe);
		} catch (javax.management.MBeanException mbe) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( mbe );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(mbe);
		} catch (javax.management.ReflectionException rfe) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( rfe );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(rfe);
		}
	}


		private void 
	stopRemoteServer() 
	{

		try {
			// get the object name for servers config mBean
			ObjectName on = DomainStatusHelper.getServersConfigObjectName();

			// invoke stop method on servers config mBean

			// get mBean server
			final MBeanServer server = getMBeanServer();

			// form the parameters
			Object[] params = new Object[1];
                	params[0] = getServerName();
                	String[] signature = {"java.lang.String"};

			// invoke the start method
                	server.invoke(on, "stopServerInstance", params, signature);
		} catch (javax.management.MalformedObjectNameException mfone) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( mfone );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(mfone);
		} catch (javax.management.InstanceNotFoundException infe) {
			// in case of PE and DAS 
			// it is desit]rable that the instance be not stopped
			// hence the log level is fine
                        final Throwable rootCause       = ExceptionUtil.getRootCause( infe );
                        getMBeanLogger().fine( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(infe);
		} catch (javax.management.MBeanException mbe) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( mbe );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(mbe);
		} catch (javax.management.ReflectionException rfe) {
                        final Throwable rootCause       = ExceptionUtil.getRootCause( rfe );
                        getMBeanLogger().warning( rootCause.toString() + "\n" +
                                        ExceptionUtil.getStackTrace( rootCause ) );
			throw new RuntimeException(rfe);
		}
	}

}
