/* * * Copyright (c) void.fm * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * Neither the name void.fm nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ package etm.core.configuration; import etm.core.aggregation.Aggregator; import etm.core.aggregation.BufferedThresholdAggregator; import etm.core.monitor.EtmMonitor; import etm.core.plugin.EtmPlugin; import etm.core.timer.DefaultTimer; import etm.core.timer.ExecutionTimer; import etm.core.util.Log; import etm.core.util.LogAdapter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Map; /** * Factory to create an EtmMonitor based on configuration. * * @author void.fm * @version $Revision$ */ public class EtmMonitorFactory { private static final LogAdapter LOG = Log.getLog(EtmMonitorFactory.class); private static final String[] JETM_TIMER = new String[]{ "etm.core.timer.Java15NanoTimer", "etm.core.timer.SunHighResTimer", "etm.core.timer.DefaultTimer" }; public static EtmMonitor createEtmMonitor(EtmMonitorConfig monitorConfig) throws Exception { Object obj; try { Constructor constructor = monitorConfig.getMonitorClass().getConstructor(new Class[]{ExecutionTimer.class, Aggregator.class}); obj = constructor.newInstance(new Object[]{createTimer(monitorConfig), createAggregators(monitorConfig)}); } catch (NoSuchMethodException e) { try { Constructor constructor = monitorConfig.getMonitorClass().getConstructor(new Class[]{Aggregator.class}); obj = constructor.newInstance(new Object[]{createAggregators(monitorConfig)}); } catch (NoSuchMethodException e1) { try { Constructor constructor = monitorConfig.getMonitorClass().getConstructor(new Class[]{ExecutionTimer.class}); obj = constructor.newInstance(new Object[]{createTimer(monitorConfig)}); } catch (NoSuchMethodException e2) { obj = monitorConfig.getMonitorClass().newInstance(); } } } EtmMonitor etmMonitor = (EtmMonitor) obj; List pluginConfig = monitorConfig.getPluginConfig(); if (pluginConfig != null) { addPlugins(etmMonitor, pluginConfig); } if (monitorConfig.isAutostart()) { etmMonitor.start(); // todo maybe we should add a configuration property for // shutdown hook too Runtime.getRuntime().addShutdownHook(new ShutDownHook(etmMonitor)); } return etmMonitor; } public static ExecutionTimer bestAvailableTimer() { for (int i = 0; i < JETM_TIMER.length; i++) { try { return (ExecutionTimer) instantiateClass(JETM_TIMER[i]); } catch (Exception e) { LOG.warn("Unable to instantiate execution timer '" + JETM_TIMER[i] + "'. Trying next. Cause:" + e.getMessage()); } catch (Throwable e) { // for our implementation we get a NoSuchMethodError for JDK's < 5.0 // therefore ignore, unless it's ThreadDeath if (e instanceof ThreadDeath) { throw (ThreadDeath) e; } } } return new DefaultTimer(); } private static ExecutionTimer createTimer(EtmMonitorConfig monitorConfig) throws IllegalAccessException, InstantiationException { if (monitorConfig.getTimerClass() != null) { return (ExecutionTimer) monitorConfig.getTimerClass().newInstance(); } else { return bestAvailableTimer(); } } private static Aggregator createAggregators(EtmMonitorConfig monitorConfig) throws IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException { if (monitorConfig.getAggregatorRoot() != null) { Aggregator current = (Aggregator) monitorConfig.getAggregatorRoot().getAggregatorClass().newInstance(); Map properties = monitorConfig.getAggregatorRoot().getProperties(); if (properties != null) { setProperties(current, properties); } List config = monitorConfig.getEtmAggregators(); if (config != null) { for (int i = config.size() - 1; i >= 0; i--) { EtmAggregatorConfig etmAggregator = (EtmAggregatorConfig) config.get(i); try { Constructor constructor = etmAggregator.getAggregatorClass().getConstructor(new Class[]{Aggregator.class}); current = (Aggregator) constructor.newInstance(new Object[]{current}); } catch (NoSuchMethodException e) { throw new EtmConfigurationException("Nested aggregator does not have an constructor with type Aggregator."); } properties = etmAggregator.getProperties(); if (properties != null) { setProperties(current, properties); } } } else { // always add buffering to aggregators if root aggregator does not buffer if (!current.getMetaData().isBuffering()) { current = new BufferedThresholdAggregator(current); } } return current; } else { return null; } } private static void addPlugins(EtmMonitor aEtmMonitor, List aPluginConfig) throws IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException { for (int i = 0; i < aPluginConfig.size(); i++) { EtmPluginConfig etmPluginConfig = (EtmPluginConfig) aPluginConfig.get(i); Object obj = etmPluginConfig.getPluginClass().newInstance(); if (etmPluginConfig.getProperties() != null) { setProperties(obj, etmPluginConfig.getProperties()); } aEtmMonitor.addPlugin((EtmPlugin) obj); } } private static void setProperties(Object obj, Map properties) throws IllegalAccessException, InvocationTargetException, ClassNotFoundException { // todo just improve ;) Method[] methods = obj.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; String methodName = method.getName(); if (methodName.startsWith("set") && methodName.length() >= 4) { String propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4); if (properties.containsKey(propertyName) && method.getParameterTypes().length == 1) { Object value = properties.get(propertyName); Class clazz = method.getParameterTypes()[0]; if (int.class.isAssignableFrom(clazz)) { method.invoke(obj, new Object[]{new Integer(Integer.parseInt((String) value))}); } else if (long.class.isAssignableFrom(clazz)) { method.invoke(obj, new Object[]{new Long(Long.parseLong((String) value))}); } else if (boolean.class.isAssignableFrom(clazz)) { if ("true".equals(value)) { method.invoke(obj, new Object[]{Boolean.TRUE}); } else if ("false".equals(value)) { method.invoke(obj, new Object[]{Boolean.FALSE}); } } else if (String.class.isAssignableFrom(clazz)) { method.invoke(obj, new Object[]{value}); } else if (Class.class.isAssignableFrom(clazz)) { method.invoke(obj, new Object[]{Class.forName((String) value)}); } else if (Map.class.isAssignableFrom(clazz)) { if (value instanceof Map) { method.invoke(obj, new Object[]{value}); } } else if (List.class.isAssignableFrom(clazz)) { if (value instanceof List) { method.invoke(obj, new Object[]{value}); } } } } } } private static Object instantiateClass(String className) throws Exception { Class clazz = Class.forName(className); return clazz.newInstance(); } private static class ShutDownHook extends Thread { private final EtmMonitor etmMonitor; public ShutDownHook(EtmMonitor aEtmMonitor) { etmMonitor = aEtmMonitor; } public void run() { if (etmMonitor.isStarted()) { etmMonitor.stop(); } } } }