/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 * 
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits réservés.
 * Ce logiciel est propriété de Sun Microsystems, Inc.
 * Distribué par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)MfCpuTime.java 1.9     04/10/15 SMI"
 *
 */

package com.sun.mfwk.util.instrum; 

import java.lang.reflect.Method;
import java.lang.Class;

import java.util.logging.*;
import com.sun.mfwk.util.log.MfLogService;

/**
 * This classs takes care of loading the jdk 1.5
 * java.lang.management.ThreadMXBean methods we need if we're running
 * on jdk1.5.  If we're not running on jdk1.5 so that
 * java.lang.management.ThreadMXBean is not available then it will use
 * our own implementation of the required methods:
 * isCurrentThreadCpuTimeSupported(),
 * getCurrentThreadCpuTimeMethod().
 * 
 *
 **/

public class MfCpuTime {
    
    private static final Class managementFactoryClass;
    private static final Method getThreadMBeanMethod;    
    private static final Class threadMBeanClass;
    private static final Method isCurrentThreadCpuTimeSupportedMethod;
    private static final Method getCurrentThreadCpuTimeMethod;
    private static final Method isThreadCpuTimeEnabledMethod;
    private static final Method setThreadCpuTimeEnabledMethod;
    private static Logger logger = null;
    private static boolean useNativeJavaCpuTime = false;
    private static boolean vmIs64Bit = false;

    private Object threadMBeanObject = null;

    private static final MfTimersJNI mfTimersJNI;

    /*
     * At this class load time determine if we will be able to
     * use native Java cpu time metrics or if we
     * will need to implement that ourselves.
     * Also, load any of the static classes or methods we need from
     * jdk1.5, if they are available.
     * We leave picking up the reference to the ThreadMBean object
     * to run time to avoid keeping a needless reference to it.
     */
    static{

        Class tryManagementFactoryClass = null;
        Class tryThreadMBeanClass = null;       
        Method tryGetThreadMBeanMethod = null;
        Method tryIsCurrentThreadCpuTimeSupportedMethod = null;
        Method tryGetCurrentThreadCpuTimeMethod = null;
        Method tryIsThreadCpuTimeEnabledMethod = null;
        Method trySetThreadCpuTimeEnabledMethod = null;
        MfTimersJNI tryMfTimersJNI = null;

        try{
            
            tryManagementFactoryClass = Class.forName(
                "java.lang.management.ManagementFactory");
            tryGetThreadMBeanMethod =
                tryManagementFactoryClass.getMethod("getThreadMXBean", null);
    
            tryThreadMBeanClass = 
                Class.forName("java.lang.management.ThreadMXBean");

            tryIsCurrentThreadCpuTimeSupportedMethod = 
                tryThreadMBeanClass.getMethod(
                                "isCurrentThreadCpuTimeSupported", null);
                    
            tryGetCurrentThreadCpuTimeMethod = 
                tryThreadMBeanClass.getMethod(
                                "getCurrentThreadCpuTime", null);
            
            tryIsThreadCpuTimeEnabledMethod = 
                tryThreadMBeanClass.getMethod(
                                "isThreadCpuTimeEnabled", null);
                                  
            Class boolClass = Boolean.TYPE;
            Class setThreadCpuTimeEnabledMethodArgs[]
                = { boolClass };
                        
            trySetThreadCpuTimeEnabledMethod = 
                tryThreadMBeanClass.getMethod("setThreadCpuTimeEnabled",
                                           setThreadCpuTimeEnabledMethodArgs); 
            
            useNativeJavaCpuTime = true;
            
            
        } catch(Exception e) {
            String vmPropertyString = null;

            /*
             * If for whatever reason any of the above fail, then
             * we will not try to use the native java cpu time
             * stuff.
            */
            //e.printStackTrace(); /* debug */
            tryManagementFactoryClass = null;
            tryGetThreadMBeanMethod = null;
            tryThreadMBeanClass = null;
            tryIsCurrentThreadCpuTimeSupportedMethod = null;
            tryGetCurrentThreadCpuTimeMethod = null;
            tryIsThreadCpuTimeEnabledMethod = null;
            trySetThreadCpuTimeEnabledMethod = null;

            useNativeJavaCpuTime = false;
            try{
                tryMfTimersJNI = new MfTimersJNI();
                //System.out.println("got a new MfTimersJNI(0 ok ");
            } catch(Exception evtime ) {
                if ( logger != null ) {
                    logger.warning("Failed to load MfTimersJNI()" +
                                   evtime.getMessage());
                }
            }
            try{
                vmPropertyString = System.getProperty("sun.arch.data.model");
                if ( vmPropertyString != null &&
                     vmPropertyString.equals("64") ) {
                    vmIs64Bit = true;
                } else {
                    vmIs64Bit = false;
                }
            } catch(Exception e1) {
                vmIs64Bit = false;
            }      
            //Boolean tmpBool = new Boolean(vmIs64Bit);
            //System.out.println("vmIs64Bit==" + tmpBool.toString() );

        }

        managementFactoryClass = tryManagementFactoryClass;
        getThreadMBeanMethod = tryGetThreadMBeanMethod;
        threadMBeanClass = tryThreadMBeanClass;
        isCurrentThreadCpuTimeSupportedMethod =
            tryIsCurrentThreadCpuTimeSupportedMethod;
        getCurrentThreadCpuTimeMethod = tryGetCurrentThreadCpuTimeMethod;
        isThreadCpuTimeEnabledMethod = tryIsThreadCpuTimeEnabledMethod;
        setThreadCpuTimeEnabledMethod = trySetThreadCpuTimeEnabledMethod;

        mfTimersJNI = tryMfTimersJNI;

    }//static

    public MfCpuTime (Logger loggerParam ) {
       
        logger = loggerParam;
        if ( useNativeJavaCpuTime ) {
            //System.out.println("MfCpuTime: constructor: jdk1.5 " + 
            //                 "Using native java implementation " +
            //                "to recover CPU time per thread" );
            if ( logger != null ) {
                logger.fine("MfCpuTime: constructor: jdk1.5 " + 
                               "Using native jdk1.5 ThreadMXBean class " +
                               "to recover CPU time per thread" );
            }
            try{                                   
                threadMBeanObject = 
                    getThreadMBeanMethod.invoke(managementFactoryClass,null);

            }catch(Exception e) {
                // This should not happen: we determineeed at 
                // class load time that we are in jdk1.5.x and
                // yet when we try to pick up the reference to the
                // ThreadMBean it is failing.
                // Best effort is to log the error and revert to our
                // own implementation.
                
                useNativeJavaCpuTime = false;                
                logger.warning("MfCpuTime: constructor: Failed to " + 
                               "recover the ThreadMXBean object " +
                               "reverting to CPuTime implementation" );
            }
        } else {
            // Here, do whatever we need to initialize our own
            // implementation of cpu time for this
            // object.
         
            //System.out.println("MfCpuTime: constructor: not jdk1.5 " + 
            //                 "Using MfCpuTime implementation " +
            //                 "to recover CPU time per thread" );
            if ( logger != null ) {
                logger.fine("MfCpuTime: constructor: not jdk1.5 " + 
                               "Using MfCpuTime implementation " +
                               "to recover CPU time per thread" );
            }
          
        }       
    }//MfCpuTime()

    public boolean isCurrentThreadCpuTimeSupported() {
        Boolean tmpBool = null;
                
        if ( useNativeJavaCpuTime ) {
            try {
                tmpBool = 
                    (Boolean)(isCurrentThreadCpuTimeSupportedMethod.invoke(
                     threadMBeanObject, null));
            } catch(Exception e ) {

                if ( logger != null ) {
                	logger.warning("MfCpuTime:failed to invoke " +
                                  "isCurrentThreadCpuTimeSupported");
                }
                /* This should never happen: in jdk1.5 these calls are
                 * supported, so if this does happens we want to know
                 * about it.  Hence, the decision not to, for example
                 * fall into our own 1.4 implementation if the call to
                 * the 1.5 jdk fails.
                 */
                tmpBool = new Boolean(false);
            }
        } else {
            /* Here, for the moment return true, but later
             * we will support this ourselves, via some jni calls.
            */
            if ( !vmIs64Bit ) {
                tmpBool = new Boolean(true);
            } else {
                tmpBool = new Boolean(false);
            }
        }
       
        return tmpBool.booleanValue();
    }   
    
    public boolean isThreadCpuTimeEnabled() {
        Boolean tmpBool = null;

        if ( useNativeJavaCpuTime ) {
            try {
                tmpBool =
                    (Boolean)(isThreadCpuTimeEnabledMethod.invoke(
                     threadMBeanObject, null));
            } catch(Exception e ) {

                //e.printStackTrace(); /* debug */
                if ( logger != null ) {
                    logger.warning("MfCpuTime:failed to invoke " +
                                   "isThreadCpuTimeEnabledMethod");
                }
                /* This should never happen: in jdk1.5 these calls are
                 * supported, so if this does happens we want to know
                 * about it.  Hence, the decision not to, for example
                 * fall into our own 1.4 implementation if the call to
                 * the 1.5 jdk fails.
                 */
                tmpBool = new Boolean(false);
            }
        } else {
            /* Here, for the moment return true, but later
             * we will support this ourselves, via some jni calls.
             */
            tmpBool = new Boolean(true);
        }

        return tmpBool.booleanValue();
    }

    public void setThreadCpuTimeEnabled(boolean newState) {

        if ( useNativeJavaCpuTime ) {
            try {
                Object[] args = { (Object)(new Boolean(newState)) };
                
                //System.out.println("Calling setCpuTimeEnabled...");
                setThreadCpuTimeEnabledMethod.invoke(threadMBeanObject, args);
            } catch(Exception e ) {

                //e.printStackTrace(); /* debug */
                if ( logger != null ) {
                	logger.warning("MfCpuTime:failed to invoke " +
                                  "setThreadCpuTimeEnabled");
                }
                /* This should never happen: in jdk1.5 these calls are
                 * supported, so if this does happens we want to know
                 * about it.  Hence, the decision not to, for example
                 * fall into our own 1.4 implementation if the call to
                 * the 1.5 jdk fails.
                 */                
            }
        } else {
            /* Here, for the moment do nothing, but later
             * we will support this ourselves, via some jni calls.
            */
            
        }
                   
    }

    public long getCurrentThreadCpuTime() {
        Long tmpLong = null;

        if ( useNativeJavaCpuTime ) {
            try {
                tmpLong = 
                    (Long)(getCurrentThreadCpuTimeMethod.invoke(
                     threadMBeanObject, null));
            } catch(Exception e ) {

                if ( logger != null ) {
                	logger.warning("MfCpuTime:failed to invoke " +
                                  "getCurrentThreadCpuTimeMethod");
                }
                /* This should never happen: in jdk1.5 these calls are
                 * supported, so if this does happens we want to know
                 * about it.  Hence, the decision not to, for example
                 * fall into our own 1.4 implementation if the call to
                 * the 1.5 jdk fails.
                 */
                tmpLong = new Long(0);
            }
        } else {
            /* Here, for the moment return 0, but later
             * we will support this ourselves, via some jni calls.
            */
            tmpLong =  new Long(mfTimersJNI.mfGethrvtime());          
        }
          
        return tmpLong.longValue();
	}
	


}
