/*
* 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 Provider
classes. Each tracing subsystem needs to provide three classes, a factory (derived from ProviderFactory
, a provider (a subclass of Provider
, and a probe type (subclass of 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 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 createProbe
method to create an appropriate ProbeSkeleton
subclass. The framework's probe class provides the implementation of the two probe methods, isEnabled()
and uncheckedTrigger()
. Both are framework-dependent implementations. Since: 1.7
/**
* 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.
Params: - method – A method in the user's interface
Returns: a subclass of ProbeSkeleton for the particular framework.
/**
* 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.
Params: - type – the user's interface
/**
* 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.
/**
* 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 invoke()
method of this class (until overridden by the VM). Returns: an implementation of the user's interface
/**
* 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 Provider
or Object
and we invoke the method directly. Params: - proxy – the instance whose method was invoked
- method – the method that was called
- args – the arguments passed in the call.
Returns: always null, if the method is a user-defined probe
/**
* 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) {
Class declaringClass = method.getDeclaringClass();
// not a provider subtype's own method
if (declaringClass != providerType) {
try {
// delegate only to methods declared by
// com.sun.tracing.Provider or java.lang.Object
if (declaringClass == Provider.class ||
declaringClass == Object.class) {
return method.invoke(this, args);
} else {
assert false;
}
} catch (IllegalAccessException e) {
assert false;
} catch (InvocationTargetException e) {
assert false;
}
} else {
triggerProbe(method, args);
}
return null;
}
Direct accessor for Probe
objects. Params: - m – the method corresponding to a probe
Returns: the method associated probe object, or 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.
/**
* 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.
Returns: the provider name
/**
* 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.
Params: - element – the element that was annotated, either a class or method
- annotation – the class of the annotation we're interested in
- defaultValue – the value to return if the annotation doesn't
exist, doesn't have a "value", or the value is empty.
/**
* 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.
Params: - element – the element that was annotated, either a class or method
- annotation – the class of the annotation we're interested in
- methodName – the name of the method in the annotation we wish
to call.
- defaultValue – the value to return if the annotation doesn't
exist, or we couldn't invoke the method for some reason.
Returns: the result of calling the annotation method, or the default.
/**
* 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;
}
protected void triggerProbe(Method method, Object[] args) {
if (active) {
ProbeSkeleton p = probes.get(method);
if (p != null) {
// Skips argument check -- already done by javac
p.uncheckedTrigger(args);
}
}
}
}