1 /*
   2  * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tracing;
  27 
  28 import java.lang.reflect.InvocationHandler;
  29 import java.lang.reflect.Method;
  30 import java.lang.reflect.Proxy;
  31 import java.lang.reflect.InvocationTargetException;
  32 import java.lang.reflect.AnnotatedElement;
  33 import java.lang.annotation.Annotation;
  34 import java.util.HashMap;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 
  38 import com.sun.tracing.Provider;
  39 import com.sun.tracing.Probe;
  40 import com.sun.tracing.ProviderName;
  41 
  42 /**
  43  * Provides a common code for implementation of {@code Provider} classes.
  44  *
  45  * Each tracing subsystem needs to provide three classes, a factory
  46  * (derived from {@code ProviderFactory}, a provider (a subclass of
  47  * {@code Provider}, and a probe type (subclass of {@code ProbeSkeleton}).
  48  *
  49  * The factory object takes a user-defined interface and provides an
  50  * implementation of it whose method calls will trigger probes in the
  51  * tracing framework.
  52  *
  53  * The framework's provider class, and its instances, are not seen by the
  54  * user at all -- they usually sit in the background and receive and dispatch
  55  * the calls to the user's provider interface.  The {@code ProviderSkeleton}
  56  * class provides almost all of the implementation needed by a framework
  57  * provider.  Framework providers must only provide a constructor and
  58  * disposal method, and implement the {@code createProbe} method to create
  59  * an appropriate {@code ProbeSkeleton} subclass.
  60  *
  61  * The framework's probe class provides the implementation of the two
  62  * probe methods, {@code isEnabled()} and {@code uncheckedTrigger()}.  Both are
  63  * framework-dependent implementations.
  64  *
  65  * @since 1.7
  66  */
  67 
  68 public abstract class ProviderSkeleton implements InvocationHandler, Provider {
  69 
  70     protected boolean active; // set to false after dispose() is called
  71     protected Class<? extends Provider> providerType; // user's interface
  72     protected HashMap<Method, ProbeSkeleton> probes; // methods to probes
  73 
  74 
  75     /**
  76      * Creates a framework-specific probe subtype.
  77      *
  78      * This method is implemented by the framework's provider and returns
  79      * framework-specific probes for a method.
  80      *
  81      * @param method A method in the user's interface
  82      * @return a subclass of ProbeSkeleton for the particular framework.
  83      */
  84     protected abstract ProbeSkeleton createProbe(Method method);
  85 
  86     /**
  87      * Initializes the provider.
  88      *
  89      * @param type the user's interface
  90      */
  91     protected ProviderSkeleton(Class<? extends Provider> type) {
  92         this.active = false; // in case of some error during initialization
  93         this.providerType = type;
  94         this.probes = new HashMap<Method,ProbeSkeleton>();
  95     }
  96 
  97     /**
  98      * Post-constructor initialization routine.
  99      *
 100      * Subclass instances must be initialized before they can create probes.
 101      * It is up to the factory implementations to call this after construction.
 102      */
 103     public void init() {
 104         Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
 105             public Method[] run() {
 106                 return providerType.getDeclaredMethods();
 107             }
 108         });
 109 
 110         for (Method m : methods) {
 111             if ( m.getReturnType() != Void.TYPE ) {
 112                 throw new IllegalArgumentException(
 113                    "Return value of method is not void");
 114             } else {
 115                 probes.put(m, createProbe(m));
 116             }
 117         }
 118         this.active = true;
 119     }
 120 
 121     /**
 122      * Magic routine which creates an implementation of the user's interface.
 123      *
 124      * This method creates the instance of the user's interface which is
 125      * passed back to the user.  Every call upon that interface will be
 126      * redirected to the {@code invoke()} method of this class (until
 127      * overridden by the VM).
 128      *
 129      * @return an implementation of the user's interface
 130      */
 131     @SuppressWarnings("unchecked")
 132     public <T extends Provider> T newProxyInstance() {
 133         final InvocationHandler ih = this;
 134         return AccessController.doPrivileged(new PrivilegedAction<T>() {
 135             public T run() {
 136                return (T)Proxy.newProxyInstance(providerType.getClassLoader(),
 137                    new Class<?>[] { providerType }, ih);
 138             }});
 139     }
 140 
 141     /**
 142      * Triggers a framework probe when a user interface method is called.
 143      *
 144      * This method dispatches a user interface method call to the appropriate
 145      * probe associated with this framework.
 146      *
 147      * If the invoked method is not a user-defined member of the interface,
 148      * then it is a member of {@code Provider} or {@code Object} and we
 149      * invoke the method directly.
 150      *
 151      * @param proxy the instance whose method was invoked
 152      * @param method the method that was called
 153      * @param args the arguments passed in the call.
 154      * @return always null, if the method is a user-defined probe
 155      */
 156     public Object invoke(Object proxy, Method method, Object[] args) {
 157         if (method.getDeclaringClass() != providerType) {
 158             try {
 159                 return method.invoke(this, args);
 160             } catch (IllegalAccessException e) {
 161                 assert false;
 162             } catch (InvocationTargetException e) {
 163                 assert false;
 164             }
 165         } else if (active) {
 166             ProbeSkeleton p = probes.get(method);
 167             if (p != null) {
 168                 // Skips argument check -- already done by javac
 169                 p.uncheckedTrigger(args);
 170             }
 171         }
 172         return null;
 173     }
 174 
 175     /**
 176      * Direct accessor for {@code Probe} objects.
 177      *
 178      * @param m the method corresponding to a probe
 179      * @return the method associated probe object, or null
 180      */
 181     public Probe getProbe(Method m) {
 182         return active ? probes.get(m) : null;
 183     }
 184 
 185     /**
 186      * Default provider disposal method.
 187      *
 188      * This is overridden in subclasses as needed.
 189      */
 190     public void dispose() {
 191         active = false;
 192         probes.clear();
 193     }
 194 
 195     /**
 196      * Gets the user-specified provider name for the user's interface.
 197      *
 198      * If the user's interface has a {@ProviderName} annotation, that value
 199      * is used.  Otherwise we use the simple name of the user interface's class.
 200      * @return the provider name
 201      */
 202     protected String getProviderName() {
 203         return getAnnotationString(
 204                 providerType, ProviderName.class, providerType.getSimpleName());
 205     }
 206 
 207     /**
 208      * Utility method for getting a string value from an annotation.
 209      *
 210      * Used for getting a string value from an annotation with a 'value' method.
 211      *
 212      * @param element the element that was annotated, either a class or method
 213      * @param annotation the class of the annotation we're interested in
 214      * @param defaultValue the value to return if the annotation doesn't
 215      * exist, doesn't have a "value", or the value is empty.
 216      */
 217     protected static String getAnnotationString(
 218             AnnotatedElement element, Class<? extends Annotation> annotation,
 219             String defaultValue) {
 220         String ret = (String)getAnnotationValue(
 221                 element, annotation, "value", defaultValue);
 222         return ret.isEmpty() ? defaultValue : ret;
 223     }
 224 
 225     /**
 226      * Utility method for calling an arbitrary method in an annotation.
 227      *
 228      * @param element the element that was annotated, either a class or method
 229      * @param annotation the class of the annotation we're interested in
 230      * @param methodName the name of the method in the annotation we wish
 231      * to call.
 232      * @param defaultValue the value to return if the annotation doesn't
 233      * exist, or we couldn't invoke the method for some reason.
 234      * @return the result of calling the annotation method, or the default.
 235      */
 236     protected static Object getAnnotationValue(
 237             AnnotatedElement element, Class<? extends Annotation> annotation,
 238             String methodName, Object defaultValue) {
 239         Object ret = defaultValue;
 240         try {
 241             Method m = annotation.getMethod(methodName);
 242             Annotation a = element.getAnnotation(annotation);
 243             ret = m.invoke(a);
 244         } catch (NoSuchMethodException e) {
 245             assert false;
 246         } catch (IllegalAccessException e) {
 247             assert false;
 248         } catch (InvocationTargetException e) {
 249             assert false;
 250         } catch (NullPointerException e) {
 251             assert false;
 252         }
 253         return ret;
 254     }
 255 }