View Javadoc

1   package org.apache.fulcrum.yaafi.interceptor.jamon;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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      // Avalon Service Lifecycle Implementation
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          // parse the performance monitor class name
96          this.performanceMonitorClassName = configuration.getChild("performanceMonitorClassName").getValue(DEFAULT_PERFORMANCEMONITOR_CLASSNAME);
97  
98          // parse the report file name
99          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 }