251 lines · 9.3 KB · Java
RawDownload
/* * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.tracing; import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.AnnotatedElement;import java.lang.annotation.Annotation;import java.util.HashMap;import java.security.AccessController;import java.security.PrivilegedAction; import com.sun.tracing.Provider;import com.sun.tracing.Probe;import com.sun.tracing.ProviderName; /** * Provides a common code for implementation of {@code Provider} classes. * * Each tracing subsystem needs to provide three classes, a factory * (derived from {@code ProviderFactory}, a provider (a subclass of * {@code Provider}, and a probe type (subclass of {@code ProbeSkeleton}). * * The factory object takes a user-defined interface and provides an * implementation of it whose method calls will trigger probes in the * tracing framework. * * The framework's provider class, and its instances, are not seen by the * user at all -- they usually sit in the background and receive and dispatch * the calls to the user's provider interface.  The {@code ProviderSkeleton} * class provides almost all of the implementation needed by a framework * provider.  Framework providers must only provide a constructor and * disposal method, and implement the {@code createProbe} method to create * an appropriate {@code ProbeSkeleton} subclass. * * The framework's probe class provides the implementation of the two * probe methods, {@code isEnabled()} and {@code uncheckedTrigger()}.  Both are * framework-dependent implementations. * * @since 1.7 */ public abstract class ProviderSkeleton implements InvocationHandler, Provider {     protected boolean active; // set to false after dispose() is called    protected Class<? extends Provider> providerType; // user's interface    protected HashMap<Method, ProbeSkeleton> probes; // methods to probes      /**     * Creates a framework-specific probe subtype.     *     * This method is implemented by the framework's provider and returns     * framework-specific probes for a method.     *     * @param method A method in the user's interface     * @return a subclass of ProbeSkeleton for the particular framework.     */    protected abstract ProbeSkeleton createProbe(Method method);     /**     * Initializes the provider.     *     * @param type the user's interface     */    protected ProviderSkeleton(Class<? extends Provider> type) {        this.active = false; // in case of some error during initialization        this.providerType = type;        this.probes = new HashMap<Method,ProbeSkeleton>();    }     /**     * Post-constructor initialization routine.     *     * Subclass instances must be initialized before they can create probes.     * It is up to the factory implementations to call this after construction.     */    public void init() {        Method[] methods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {            public Method[] run() {                return providerType.getDeclaredMethods();            }        });         for (Method m : methods) {            if ( m.getReturnType() != Void.TYPE ) {                throw new IllegalArgumentException(                   "Return value of method is not void");            } else {                probes.put(m, createProbe(m));            }        }        this.active = true;    }     /**     * Magic routine which creates an implementation of the user's interface.     *     * This method creates the instance of the user's interface which is     * passed back to the user.  Every call upon that interface will be     * redirected to the {@code invoke()} method of this class (until     * overridden by the VM).     *     * @return an implementation of the user's interface     */    @SuppressWarnings("unchecked")    public <T extends Provider> T newProxyInstance() {        return (T)Proxy.newProxyInstance(providerType.getClassLoader(),               new Class<?>[] { providerType }, this);    }     /**     * Triggers a framework probe when a user interface method is called.     *     * This method dispatches a user interface method call to the appropriate     * probe associated with this framework.     *     * If the invoked method is not a user-defined member of the interface,     * then it is a member of {@code Provider} or {@code Object} and we     * invoke the method directly.     *     * @param proxy the instance whose method was invoked     * @param method the method that was called     * @param args the arguments passed in the call.     * @return always null, if the method is a user-defined probe     */    public Object invoke(Object proxy, Method method, Object[] args) {        if (method.getDeclaringClass() != providerType) {            try {                return method.invoke(this, args);            } catch (IllegalAccessException e) {                assert false;            } catch (InvocationTargetException e) {                assert false;            }        } else if (active) {            ProbeSkeleton p = probes.get(method);            if (p != null) {                // Skips argument check -- already done by javac                p.uncheckedTrigger(args);            }        }        return null;    }     /**     * Direct accessor for {@code Probe} objects.     *     * @param m the method corresponding to a probe     * @return the method associated probe object, or null     */    public Probe getProbe(Method m) {        return active ? probes.get(m) : null;    }     /**     * Default provider disposal method.     *     * This is overridden in subclasses as needed.     */    public void dispose() {        active = false;        probes.clear();    }     /**     * Gets the user-specified provider name for the user's interface.     *     * If the user's interface has a {@ProviderName} annotation, that value     * is used.  Otherwise we use the simple name of the user interface's class.     * @return the provider name     */    protected String getProviderName() {        return getAnnotationString(                providerType, ProviderName.class, providerType.getSimpleName());    }     /**     * Utility method for getting a string value from an annotation.     *     * Used for getting a string value from an annotation with a 'value' method.     *     * @param element the element that was annotated, either a class or method     * @param annotation the class of the annotation we're interested in     * @param defaultValue the value to return if the annotation doesn't     * exist, doesn't have a "value", or the value is empty.     */    protected static String getAnnotationString(            AnnotatedElement element, Class<? extends Annotation> annotation,            String defaultValue) {        String ret = (String)getAnnotationValue(                element, annotation, "value", defaultValue);        return ret.isEmpty() ? defaultValue : ret;    }     /**     * Utility method for calling an arbitrary method in an annotation.     *     * @param element the element that was annotated, either a class or method     * @param annotation the class of the annotation we're interested in     * @param methodName the name of the method in the annotation we wish     * to call.     * @param defaultValue the value to return if the annotation doesn't     * exist, or we couldn't invoke the method for some reason.     * @return the result of calling the annotation method, or the default.     */    protected static Object getAnnotationValue(            AnnotatedElement element, Class<? extends Annotation> annotation,            String methodName, Object defaultValue) {        Object ret = defaultValue;        try {            Method m = annotation.getMethod(methodName);            Annotation a = element.getAnnotation(annotation);            ret = m.invoke(a);        } catch (NoSuchMethodException e) {            assert false;        } catch (IllegalAccessException e) {            assert false;        } catch (InvocationTargetException e) {            assert false;        } catch (NullPointerException e) {            assert false;        }        return ret;    }}