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 }