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 }