1 /* |
|
2 * Copyright (c) 2008, 2014, 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 Class<?> declaringClass = method.getDeclaringClass(); |
|
158 // not a provider subtype's own method |
|
159 if (declaringClass != providerType) { |
|
160 try { |
|
161 // delegate only to methods declared by |
|
162 // com.sun.tracing.Provider or java.lang.Object |
|
163 if (declaringClass == Provider.class || |
|
164 declaringClass == Object.class) { |
|
165 return method.invoke(this, args); |
|
166 } else { |
|
167 // assert false : "this should never happen" |
|
168 // reaching here would indicate a breach |
|
169 // in security in the higher layers |
|
170 throw new SecurityException(); |
|
171 } |
|
172 } catch (IllegalAccessException e) { |
|
173 assert false; |
|
174 } catch (InvocationTargetException e) { |
|
175 assert false; |
|
176 } |
|
177 } else { |
|
178 triggerProbe(method, args); |
|
179 } |
|
180 return null; |
|
181 } |
|
182 |
|
183 /** |
|
184 * Direct accessor for {@code Probe} objects. |
|
185 * |
|
186 * @param m the method corresponding to a probe |
|
187 * @return the method associated probe object, or null |
|
188 */ |
|
189 public Probe getProbe(Method m) { |
|
190 return active ? probes.get(m) : null; |
|
191 } |
|
192 |
|
193 /** |
|
194 * Default provider disposal method. |
|
195 * |
|
196 * This is overridden in subclasses as needed. |
|
197 */ |
|
198 public void dispose() { |
|
199 active = false; |
|
200 probes.clear(); |
|
201 } |
|
202 |
|
203 /** |
|
204 * Gets the user-specified provider name for the user's interface. |
|
205 * |
|
206 * If the user's interface has a {@ProviderName} annotation, that value |
|
207 * is used. Otherwise we use the simple name of the user interface's class. |
|
208 * @return the provider name |
|
209 */ |
|
210 protected String getProviderName() { |
|
211 return getAnnotationString( |
|
212 providerType, ProviderName.class, providerType.getSimpleName()); |
|
213 } |
|
214 |
|
215 /** |
|
216 * Utility method for getting a string value from an annotation. |
|
217 * |
|
218 * Used for getting a string value from an annotation with a 'value' method. |
|
219 * |
|
220 * @param element the element that was annotated, either a class or method |
|
221 * @param annotation the class of the annotation we're interested in |
|
222 * @param defaultValue the value to return if the annotation doesn't |
|
223 * exist, doesn't have a "value", or the value is empty. |
|
224 */ |
|
225 protected static String getAnnotationString( |
|
226 AnnotatedElement element, Class<? extends Annotation> annotation, |
|
227 String defaultValue) { |
|
228 String ret = (String)getAnnotationValue( |
|
229 element, annotation, "value", defaultValue); |
|
230 return ret.isEmpty() ? defaultValue : ret; |
|
231 } |
|
232 |
|
233 /** |
|
234 * Utility method for calling an arbitrary method in an annotation. |
|
235 * |
|
236 * @param element the element that was annotated, either a class or method |
|
237 * @param annotation the class of the annotation we're interested in |
|
238 * @param methodName the name of the method in the annotation we wish |
|
239 * to call. |
|
240 * @param defaultValue the value to return if the annotation doesn't |
|
241 * exist, or we couldn't invoke the method for some reason. |
|
242 * @return the result of calling the annotation method, or the default. |
|
243 */ |
|
244 protected static Object getAnnotationValue( |
|
245 AnnotatedElement element, Class<? extends Annotation> annotation, |
|
246 String methodName, Object defaultValue) { |
|
247 Object ret = defaultValue; |
|
248 try { |
|
249 Method m = annotation.getMethod(methodName); |
|
250 Annotation a = element.getAnnotation(annotation); |
|
251 ret = m.invoke(a); |
|
252 } catch (NoSuchMethodException e) { |
|
253 assert false; |
|
254 } catch (IllegalAccessException e) { |
|
255 assert false; |
|
256 } catch (InvocationTargetException e) { |
|
257 assert false; |
|
258 } catch (NullPointerException e) { |
|
259 assert false; |
|
260 } |
|
261 return ret; |
|
262 } |
|
263 |
|
264 protected void triggerProbe(Method method, Object[] args) { |
|
265 if (active) { |
|
266 ProbeSkeleton p = probes.get(method); |
|
267 if (p != null) { |
|
268 // Skips argument check -- already done by javac |
|
269 p.uncheckedTrigger(args); |
|
270 } |
|
271 } |
|
272 } |
|
273 } |
|