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