001 package org.apache.fulcrum.yaafi.interceptor.jamon;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.avalon.framework.activity.Disposable;
023 import org.apache.avalon.framework.activity.Initializable;
024 import org.apache.avalon.framework.configuration.Configuration;
025 import org.apache.avalon.framework.configuration.ConfigurationException;
026 import org.apache.avalon.framework.configuration.Reconfigurable;
027 import org.apache.avalon.framework.thread.ThreadSafe;
028 import org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext;
029 import org.apache.fulcrum.yaafi.framework.reflection.Clazz;
030 import org.apache.fulcrum.yaafi.interceptor.baseservice.BaseInterceptorServiceImpl;
031
032 import java.io.File;
033 import java.io.FileOutputStream;
034 import java.io.PrintWriter;
035 import java.lang.reflect.Method;
036
037 /**
038 * A service using JAMon for performance monitoring. The implementation
039 * relies on reflection to invoke JAMON to avoid compile-time coupling.
040 *
041 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
042 */
043
044 public class JamonInterceptorServiceImpl
045 extends BaseInterceptorServiceImpl
046 implements JamonInterceptorService, Reconfigurable, ThreadSafe, Disposable, Initializable
047 {
048 /** are the JAMon classes in the classpath */
049 private boolean isJamonAvailable;
050
051 /** the file to hold the report */
052 private File reportFile;
053
054 /** the time in ms between two reports */
055 private long reportTimeout;
056
057 /** do we create a report during disposal of the service */
058 private boolean reportOnExit;
059
060 /** the time when the next report is due */
061 private long nextReportTimestamp;
062
063 /** the implementation class name for the performance monitor */
064 private String performanceMonitorClassName;
065
066 /** the implementation class name for the performance monitor */
067 private Class performanceMonitorClass;
068
069 /** the class name of the JAMon MonitorFactory */
070 private static final String MONITORFACTORY_CLASSNAME = "com.jamonapi.MonitorFactory";
071
072 /** the class name of the JAMon MonitorFactory */
073 private static final String DEFAULT_PERFORMANCEMONITOR_CLASSNAME = "org.apache.fulcrum.yaafi.interceptor.jamon.Jamon1PerformanceMonitorImpl";
074
075 /////////////////////////////////////////////////////////////////////////
076 // Avalon Service Lifecycle Implementation
077 /////////////////////////////////////////////////////////////////////////
078
079 /**
080 * Constructor
081 */
082 public JamonInterceptorServiceImpl()
083 {
084 super();
085 }
086
087 /**
088 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
089 */
090 public void configure(Configuration configuration) throws ConfigurationException
091 {
092 super.configure(configuration);
093 this.reportTimeout = configuration.getChild("reportTimeout").getValueAsLong(0);
094
095 // parse the performance monitor class name
096 this.performanceMonitorClassName = configuration.getChild("performanceMonitorClassName").getValue(DEFAULT_PERFORMANCEMONITOR_CLASSNAME);
097
098 // parse the report file name
099 String reportFileName = configuration.getChild("reportFile").getValue("./jamon.html");
100 this.reportFile = this.makeAbsoluteFile( reportFileName );
101
102 // determine when to create the next report
103 this.nextReportTimestamp = System.currentTimeMillis() + this.reportTimeout;
104
105 // do we create a report on disposal
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 // load the performance monitor class
133 this.performanceMonitorClass = Clazz.getClazz(this.getClassLoader(), this.performanceMonitorClassName);
134
135 // check if we can create an instance of the performance monitor class
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 // Service interface implementation
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 // Service Implementation
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 }