All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.pfl.tf.spi.MethodMonitorRegistry Maven / Gradle / Ivy

There is a newer version: 4.0.4
Show newest version
/*
 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.glassfish.pfl.tf.spi;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.glassfish.pfl.basic.contain.SynchronizedHolder;
import org.glassfish.pfl.tf.spi.annotation.MethodMonitorGroup;

/** Main class for registering MethodMonitorFactories against particular
 * annotation classes that represent method monitor groups.  This
 *
 * @author ken
 */
public class MethodMonitorRegistry {
    private static final Set mmAnnotations =
	new HashSet() ;

    /** Merges the behaviors of the method monitors together.
     *
     */
    public synchronized static MethodMonitor merge( MethodMonitor[] mms) {
        return null ;
    }

    public synchronized static Set getMMAnnotations() {
	return new HashSet( mmAnnotations ) ;
    }

    public synchronized static void registerAnnotationFile( final String fname ) {
	try {
	    // Read tracing annotation property file into mmAnnotations.
	    final ResourceBundle rb = ResourceBundle.getBundle( fname ) ;
	    String obj = rb.getString( "org.glassfish.tf.annotations.size" ) ;
	    int size = 0 ;
	    if (obj != null) {
		size = Integer.valueOf( obj ) ;
	    }

	    for (int ctr=1; ctr<=size; ctr++) {
		obj = rb.getString( "org.glassfish.tf.annotation." + ctr ) ;
		mmAnnotations.add( obj ) ;
	    }
	} catch (Exception exc) {
	    System.out.println( "Exception: " + exc ) ;
	}
    }

    // Maps traceables class to the list of TimingPointTypes corresponding to
    // the method name (as in classToMNames).  The type for all MM names is
    // NONE, while the type for info maethods InfoMethods is taken from the 
    // @InfoMethod annotation.
    private static final Map,List> classToTimerTypes =
        new HashMap,List>() ;

    private static final Map,List> classToTimerNames =
        new HashMap,List>() ;

    // Maps traceable classes to the list of method names (which is in the order
    // used in the generated code, so the index of a method name is the number
    // used in the generated code).
    private static final Map,List> classToMNames =
        new HashMap,List>() ;
    
    // Maps traceable classes to a Map from Annotation class to the 
    // MethodMonitor Holder, which allows easy and safe updates to the
    // MethodMonitor.
    private static final Map,
        Map,
            SynchronizedHolder>> classToAnnoMM =

            new HashMap,
                Map,
                    SynchronizedHolder>>() ;

    // For each MM Annotation, lists all of the immediate subgroups.
    private static final Map,
        Set>> subgroups =
        new HashMap,
            Set>>() ;

    // For each MM Annotation, lists all MM annotations reachable via subgroups.
    // This is the reflexive, transitive closure of subgroups.
    private static final Map,
        Set>> subgroupsTC =
        new HashMap,
            Set>>() ;
    
    // For each MM Annotation, lists all traceable Classes that
    // have that annotation.
    private static final Map,
        Set>> annotationToClasses =
        new HashMap,Set>>() ;

    // For each MM Annotation, give the registered MethodMonitorFactory (if any)
    private static final Map,
        MethodMonitorFactory> annotationToMMF =
        new HashMap,MethodMonitorFactory>() ; 

    // For each MM Annotation a, give the set of all MethodMonitorFactory 
    // instances that are registered to any element of subgroupsTC(a).
    private static final Map,
        Set> annotationToMMFSets =
        new HashMap,Set>() ;

    // For each MM Annotation a, give the composition of annotationToMMFSets(a).
    private static final Map,
        MethodMonitorFactory> annotationToMMFComposition =
        new HashMap,MethodMonitorFactory>() ;

    private static void updateTracedClass( Class cls ) {
        Map,SynchronizedHolder> map =
            classToAnnoMM.get( cls ) ; 

        for (Map.Entry, 
            SynchronizedHolder> entry : map.entrySet() ) {

            MethodMonitorFactory mmf =
                annotationToMMFComposition.get( entry.getKey() ) ;

            if (mmf == null) {
                entry.getValue().content( null ) ;
            } else {
                entry.getValue().content( mmf.create( cls )) ;
            }
        }

    }

    private static void updateAnnotation( Class annot ) {
        // update annotationToMMFSets from annotationToMMF and subgroupsTC
        Set mmfs = new HashSet() ;
        annotationToMMFSets.put( annot, mmfs ) ;

        final Set> relatedAnnos =
            subgroupsTC.get( annot ) ;
        for (Class key : relatedAnnos) {
            MethodMonitorFactory mmf = annotationToMMF.get( key ) ;
            if (mmf != null) {
                mmfs.add( mmf ) ;
            }
        }

        // update annotationsToMMFComposition from annotationToMMFSets
        annotationToMMFComposition.put( annot,
            MethodMonitorFactoryDefaults.compose( mmfs ) ) ;

        // update the classes that are annotated by this annotation.
        final Set> classes = annotationToClasses.get(annot) ;
        if (classes != null) {
            for (Class cls : classes) {
                updateTracedClass( cls ) ;
            }
        }
    }

    // Called after the subgroups relation has changed.  This forces 
    // recomputation of annotationToMMFSets and annotationsToMMFComposition,
    // and also updates to all registered classes in the
    // annotationToClasses map.
    private static void doFullUpdate() {
        for (Class annot : annotationToMMF.keySet() ) {
            updateAnnotation( annot ) ;
        }

        for (Class key : classToAnnoMM.keySet()) {
            updateTracedClass( key ) ;
        }
    }

    private static boolean scanClassAnnotations( final Class cls ) {
        boolean updated = false ;
        boolean hasMMAnnotation = false ;
        for (Annotation anno : cls.getAnnotations()) { 
            final Class annoClass =
                anno.annotationType() ;
            final MethodMonitorGroup mmg =
                annoClass.getAnnotation( MethodMonitorGroup.class ) ;

            if (mmg != null) {
                hasMMAnnotation = true ;
                Set> target = annotationToClasses.get( annoClass ) ;
                if (target == null) {
                    target = new HashSet>() ;
                    annotationToClasses.put( annoClass, target ) ;
                }
                target.add( cls ) ;

                if (scanAnnotation( annoClass, mmg ) ) {
                    updated = true ;
                }
            }
        }

        if (!hasMMAnnotation) {
            throw new RuntimeException( "Class " + cls + " is not traceable" ) ;
        }

        return updated ;
    }

    private static boolean scanAnnotation(
        final Class annoClass,
        final MethodMonitorGroup mmg ) {

        boolean updated = false ;

        if (!subgroups.containsKey( annoClass )) {
            updated = true ;
            Set> acs =
                new HashSet>( Arrays.asList(
                mmg.value() ) ) ;
            subgroups.put( annoClass, acs ) ;

            computeTransitiveClosure() ;
        }

        return updated ;
    }

    private static void computeTransitiveClosure() {
        subgroupsTC.clear() ;
        for (Class anno : subgroups.keySet()) {
            Set> memset =
                new HashSet>() ;
            subgroupsTC.put( anno, memset ) ;
        }

        for (Class anno : subgroupsTC.keySet()) {
            dfs( anno, anno ) ;
        }
    }

    private static void dfs( Class src,
        Class dest ) {

        Set> images = subgroupsTC.get( src ) ;
        images.add( dest ) ;

        Set> temp = subgroups.get(dest) ;
        if (temp != null) {
            for (Class anno : temp) {
                if (!images.contains( anno )) {
                    dfs( src, anno ) ;
                }
            }
        }
    }

    private static String getExternalName( String name ) {
	return name.replace( '/', '.' ) ;
    }

    private static final MethodMonitorGroup checkAnnotation(
        Class annoClass ) {

        final MethodMonitorGroup mmg =
            annoClass.getAnnotation( MethodMonitorGroup.class ) ;

        if (mmg == null) {
            throw new RuntimeException( "Annotation " + annoClass
                + " does not have the MethodMonitorGroup annotation" ) ;
        } else {
            return mmg ;
        }
    }

    /** Register a class with the tracing facility.  The class must be an
     * instrumented class that is annotated with an annotation with a
     * meta-annotation of @MethodMonitorGroup.  Note that this method should
     * only be called from the enhanced class, not directly by the user.
     *
     * @param cls  Class to register, which must have 1 or more MM annotations.
     * @param methodNames The list of method names used in the enhanced code.
     * The index of the name is the value used in the method.
     * @param annoMM The MM holders for each MM annotation on the class.
     */
    public synchronized static void registerClass( final Class cls,
        final List methodNames,
        final Map,
            SynchronizedHolder> annoMM ) {

        final boolean fullUpdate = scanClassAnnotations( cls ) ;

        classToMNames.put( cls, methodNames ) ;
        classToAnnoMM.put( cls, annoMM ) ;

        if (fullUpdate) {
            doFullUpdate() ;
        } else {
            updateTracedClass( cls ) ;
        }
    }

    /** Register a class with the tracing facility.  This form assumes that
     * all of the computation for method names and the mapping from annotation
     * name to MM holder is done at registration time, rather than in the 
     * bytecode enhancer.  This shortens the generated bytecode noticeably.
     * @param cls
     */
    public synchronized static void registerClass( final Class cls ) {

	Util util = new Util( false, 0 ) ;
	EnhancedClassData ecd = new EnhancedClassDataReflectiveImpl( 
	    util, cls) ;

        final boolean fullUpdate = scanClassAnnotations( cls ) ;

	classToMNames.put( cls, ecd.getMethodNames() ) ;
        classToTimerTypes.put( cls, ecd.getTimingPointTypes() ) ;
        classToTimerNames.put( cls, ecd.getTimingPointNames() ) ;

        final Map,
            SynchronizedHolder> annoMM =
	    new HashMap,
	        SynchronizedHolder>() ;

	for (Map.Entry entry :
	    ecd.getAnnotationToHolderName().entrySet() ) {

	    try {
		final String aname = entry.getKey() ;	// annotation name
		final String fname = entry.getValue() ;	// field name

		final Field fld = cls.getDeclaredField( fname ) ;

                // XXX needs doPrivileged if non-null SecurityManager
		fld.setAccessible(true) ;

		final SynchronizedHolder sh =
		    new SynchronizedHolder() ;

	        fld.set( null, sh) ;

                final String axname = getExternalName( aname ) ;

                // This assumes that the class and its annotations are all available in
                // the same ClassLoader.  In OSGi, this means that the class and its
                // annotations are in the same OSGi bundle, which is a reasonable
                // restriction.
                Class aclass =
                    (Class)Class.forName( axname, true,
                        cls.getClassLoader() ) ;

		annoMM.put( aclass, sh ) ;
	    } catch (Exception exc) {
		System.out.println( "Exceptionn MethodMonitorRegistry.registerClass: " + exc ) ;
	    }
	}

        classToAnnoMM.put( cls, annoMM ) ;

        if (fullUpdate) {
            doFullUpdate() ;
        } else {
            updateTracedClass( cls ) ;
        }
    }

    public synchronized static List getMethodNames( Class cls ) {
        return classToMNames.get( cls ) ;    
    }

    /** Provided so that implementation of the MethodMonitor interface can
     * obtain the method name for use in log reports or for other purposes.
     * 
     * @param cls The enhanced class
     * @param identifier An Integer representing the method name.
     * @return The name of the method corresponding to the identifier.
     */
    public synchronized static String getMethodName( Class cls, int identifier ) {
        List names = classToMNames.get( cls ) ;

        if (names == null) {
            throw new RuntimeException( "Class " + cls + " not found in map" ) ;
        }

        if (identifier < 0 || identifier >= names.size()) {
            throw new RuntimeException( "identifier is out of range" ) ;
        }

        return names.get( identifier ) ;
    }

    public synchronized static int getMethodIdentifier( Class cls, String mname ) {
        List names = classToMNames.get( cls ) ;

        if (names == null) {
            throw new RuntimeException( "Class " + cls + " not found in map" ) ;
        }

        for (int ctr=0; ctr annot,
        MethodMonitorFactory mmf ) {

        final boolean fullUpdate = scanAnnotation( annot,
            checkAnnotation( annot ) );

        annotationToMMF.put( annot, mmf ) ;

        if (fullUpdate) {
            doFullUpdate() ;
        } else {
            updateAnnotation( annot ) ;
        }
    }

    /** Remove the MethodMonitorFactory (if any) that is associated with annot.
     * 
     * @param annot
     */
    public synchronized static void clear( Class annot ) {

        final boolean fullUpdate = scanAnnotation( annot,
            checkAnnotation( annot ) );

        annotationToMMF.remove( annot ) ;

        if (fullUpdate) {
            doFullUpdate() ;
        } else {
            updateAnnotation( annot ) ;
        }
    }

    /** Return the MethodMonitorFactory registered against the annotation, or
     * null if nothing is registered.
     *
     * @param annot A class representing an annotation, which must itself
     * have a MethodMonitorGroup meta-annotation.
     * @return The MethodMonitorFactory for annot.
     */
    public synchronized static MethodMonitorFactory registeredFactory(
        Class annot ) {

        final boolean fullUpdate = scanAnnotation( annot,
            checkAnnotation( annot ) );

        if (fullUpdate) {
            doFullUpdate() ;
        }

        return annotationToMMF.get( annot ) ;
    }

    /** Return the current MethodMonitor in use for the given cls and annot.
     * Returns null if no MethodMonitor is in use. Throws an exception if
     * either cls is not a traced class, or annot is not a tracing annotation
     * on cls.
     *
     * @param cls The Traced class.
     * @param annot A trace annotation on cls.
     * @return The MethodMonitor, if any.
     */
    public synchronized static MethodMonitor getMethodMonitorForClass( final Class cls,
        final Class annot ) {
        Map,SynchronizedHolder> map =
            classToAnnoMM.get( cls ) ;

        if (map == null) {
            throw new RuntimeException( "Class "
                + cls + " is not a traced class.") ;
        }

        SynchronizedHolder holder = map.get( annot ) ;

        if (holder == null) {
            throw new RuntimeException( "Annotation " + annot
                + " is not a tracing annotation defined on class " + cls ) ;
        }

        return holder.content() ;
    }

    /** Return a list of all timer types defined for cls.  This is in the same
     * order as classToMNames.get(cls).
     * @param cls The monitored class to use.
     * @return A list of timer types in the same order as the method names.
     */
    public synchronized static List getTimerTypes( final Class cls ) {
        return classToTimerTypes.get( cls ) ;
    }

    public synchronized static List getTimerNames( final Class cls ) {
        return classToTimerNames.get( cls ) ;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy