1 package org.apache.fulcrum.yaafi.interceptor.jamon;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.avalon.framework.activity.Disposable;
23 import org.apache.avalon.framework.activity.Initializable;
24 import org.apache.avalon.framework.configuration.Configuration;
25 import org.apache.avalon.framework.configuration.ConfigurationException;
26 import org.apache.avalon.framework.configuration.Reconfigurable;
27 import org.apache.avalon.framework.thread.ThreadSafe;
28 import org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext;
29 import org.apache.fulcrum.yaafi.framework.reflection.Clazz;
30 import org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl;
31
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.PrintWriter;
35 import java.lang.reflect.Method;
36
37 /**
38 * A service using JAMon for performance monitoring. The implementation
39 * relies on reflection to invoke JAMON to avoid compile-time coupling.
40 *
41 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
42 */
43
44 public class JamonInterceptorServiceImpl
45 extends BaseInterceptorServiceImpl
46 implements JamonInterceptorService, Reconfigurable, ThreadSafe, Disposable, Initializable
47 {
48 /** are the JAMon classes in the classpath */
49 private boolean isJamonAvailable;
50
51 /** the file to hold the report */
52 private File reportFile;
53
54 /** the time in ms between two reports */
55 private long reportTimeout;
56
57 /** do we create a report during disposal of the service */
58 private boolean reportOnExit;
59
60 /** the time when the next report is due */
61 private long nextReportTimestamp;
62
63 /** the implementation class name for the performance monitor */
64 private String performanceMonitorClassName;
65
66 /** the implementation class name for the performance monitor */
67 private Class performanceMonitorClass;
68
69 /** the class name of the JAMon MonitorFactory */
70 private static final String MONITORFACTORY_CLASSNAME = "com.jamonapi.MonitorFactory";
71
72 /** the class name of the JAMon MonitorFactory */
73 private static final String DEFAULT_PERFORMANCEMONITOR_CLASSNAME = "org.apache.fulcrum.yaafi.interceptor.jamon.Jamon1PerformanceMonitorImpl";
74
75
76
77
78
79 /**
80 * Constructor
81 */
82 public JamonInterceptorServiceImpl()
83 {
84 super();
85 }
86
87 /**
88 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
89 */
90 public void configure(Configuration configuration) throws ConfigurationException
91 {
92 super.configure(configuration);
93 this.reportTimeout = configuration.getChild("reportTimeout").getValueAsLong(0);
94
95
96 this.performanceMonitorClassName = configuration.getChild("performanceMonitorClassName").getValue(DEFAULT_PERFORMANCEMONITOR_CLASSNAME);
97
98
99 String reportFileName = configuration.getChild("reportFile").getValue("./jamon.html");
100 this.reportFile = this.makeAbsoluteFile( reportFileName );
101
102
103 this.nextReportTimestamp = System.currentTimeMillis() + this.reportTimeout;
104
105
106 this.reportOnExit = configuration.getChild("reportOnExit").getValueAsBoolean(false);
107 }
108
109 /**
110 * @see org.apache.avalon.framework.activity.Initializable#initialize()
111 */
112 public void initialize() throws Exception
113 {
114 ClassLoader classLoader = this.getClassLoader();
115
116 if (!Clazz.hasClazz(classLoader, MONITORFACTORY_CLASSNAME))
117 {
118 String msg = "The JamonInterceptorService is disabled since the JAMON classes are not found in the classpath";
119 this.getLogger().warn(msg);
120 this.isJamonAvailable = false;
121 return;
122 }
123
124 if (!Clazz.hasClazz(classLoader, this.performanceMonitorClassName))
125 {
126 String msg = "The JamonInterceptorService is disabled since the performance monitor class is not found in the classpath";
127 this.getLogger().warn(msg);
128 this.isJamonAvailable = false;
129 return;
130 }
131
132
133 this.performanceMonitorClass = Clazz.getClazz(this.getClassLoader(), this.performanceMonitorClassName);
134
135
136 JamonPerformanceMonitor testMonitor = this.createJamonPerformanceMonitor(null, null, true);
137 if(testMonitor == null)
138 {
139 String msg = "The JamonInterceptorService is disabled since the performance monitor can't be instantiated";
140 this.getLogger().warn(msg);
141 this.isJamonAvailable = false;
142 return;
143 }
144
145 this.getLogger().debug("The JamonInterceptorService is enabled");
146 this.isJamonAvailable = true;
147 }
148
149 /**
150 * @see org.apache.avalon.framework.configuration.Reconfigurable#reconfigure(org.apache.avalon.framework.configuration.Configuration)
151 */
152 public void reconfigure(Configuration configuration) throws ConfigurationException
153 {
154 super.reconfigure(configuration);
155 this.configure(configuration);
156 }
157
158 /**
159 * @see org.apache.avalon.framework.activity.Disposable#dispose()
160 */
161 public void dispose()
162 {
163 if( this.reportOnExit )
164 {
165 this.run();
166 }
167
168 this.reportFile = null;
169 }
170
171
172
173
174
175 /**
176 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onEntry(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext)
177 */
178 public void onEntry(AvalonInterceptorContext interceptorContext)
179 {
180 if( this.isJamonAvailable() )
181 {
182 this.writeReport();
183
184 String serviceShortHand = interceptorContext.getServiceShorthand();
185 Method serviceMethod = interceptorContext.getMethod();
186 boolean isEnabled = this.isServiceMonitored(interceptorContext );
187 JamonPerformanceMonitor monitor = this.createJamonPerformanceMonitor(serviceShortHand, serviceMethod, isEnabled);
188 monitor.start();
189 interceptorContext.getRequestContext().put(this.getServiceName(), monitor);
190 }
191 }
192
193 /**
194 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onExit(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Object)
195 */
196 public void onExit(AvalonInterceptorContext interceptorContext, Object result)
197 {
198 if( this.isJamonAvailable() )
199 {
200 JamonPerformanceMonitor monitor;
201 monitor = (JamonPerformanceMonitor) interceptorContext.getRequestContext().remove(this.getServiceName());
202 monitor.stop();
203 }
204 }
205
206 /**
207 * @see org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorService#onError(org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext, java.lang.Throwable)
208 */
209 public void onError(AvalonInterceptorContext interceptorContext,Throwable t)
210 {
211 if( this.isJamonAvailable() )
212 {
213 JamonPerformanceMonitor monitor;
214 monitor = (JamonPerformanceMonitor) interceptorContext.getRequestContext().remove(this.getServiceName());
215 monitor.stop(t);
216 }
217 }
218
219 /**
220 * Writes the JAMON report to the file system.
221 *
222 * @see java.lang.Runnable#run()
223 */
224 public void run()
225 {
226 this.writeReport(this.reportFile);
227 }
228
229
230
231
232
233 /**
234 * @return Returns the isJamonAvailable.
235 */
236 protected final boolean isJamonAvailable()
237 {
238 return this.isJamonAvailable;
239 }
240
241 /**
242 * Factory method for creating an implementation of a JamonPerformanceMonitor.
243 *
244 * @param serviceName the service name
245 * @param method the method
246 * @param isEnabled is the monitor enabled
247 * @return the instance or <b>null</b> if the creation failed
248 */
249 protected JamonPerformanceMonitor createJamonPerformanceMonitor(String serviceName, Method method, boolean isEnabled)
250 {
251 JamonPerformanceMonitor result = null;
252
253 try
254 {
255 Class[] signature = { String.class, Method.class, Boolean.class };
256 Object[] args = { serviceName, method, (isEnabled) ? Boolean.TRUE : Boolean.FALSE};
257 result = (JamonPerformanceMonitor) Clazz.newInstance(this.performanceMonitorClass, signature, args);
258 return result;
259 }
260 catch(Exception e)
261 {
262 String msg = "Failed to create a performance monitor instance : " + this.performanceMonitorClassName;
263 this.getLogger().error(msg, e);
264 return result;
265 }
266 }
267
268 /**
269 * Write a report file
270 */
271 protected void writeReport()
272 {
273 if( this.reportTimeout > 0 )
274 {
275 long currTimestamp = System.currentTimeMillis();
276
277 if( currTimestamp > this.nextReportTimestamp )
278 {
279 this.nextReportTimestamp = currTimestamp + this.reportTimeout;
280 this.writeReport(this.reportFile);
281 }
282 }
283 }
284
285 /**
286 * Write the HTML report to the given destination.
287 *
288 * @param reportFile the report destination
289 */
290 protected void writeReport( File reportFile )
291 {
292 PrintWriter printWriter = null;
293
294 if( this.isJamonAvailable() )
295 {
296 try
297 {
298 if( this.getLogger().isDebugEnabled() )
299 {
300 this.getLogger().debug( "Writing JAMOM report to " + reportFile.getAbsolutePath() );
301 }
302
303 FileOutputStream fos = new FileOutputStream( reportFile );
304 printWriter = new PrintWriter( fos );
305 JamonPerformanceMonitor monitor = this.createJamonPerformanceMonitor(null, null, true);
306 String report = monitor.createReport();
307 printWriter.write( report );
308 printWriter.close();
309 }
310 catch( Throwable t )
311 {
312 String msg = "Generating the JAMON report failed for " + reportFile.getAbsolutePath();
313 this.getLogger().error(msg,t);
314 }
315 finally
316 {
317 if( printWriter != null )
318 {
319 printWriter.close();
320 }
321 }
322 }
323 }
324 }