/* * * 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.contrib.integration.cdi; import etm.contrib.integration.cdi.common.spi.AbstractCdiBean; import etm.contrib.integration.cdi.common.spi.DelegatingAnnotatedMethod; import etm.contrib.integration.cdi.common.spi.DelegatingAnnotatedType; import etm.core.configuration.BasicEtmConfigurator; import etm.core.configuration.EtmManager; import etm.core.configuration.EtmMonitorConfig; import etm.core.configuration.EtmMonitorFactory; import etm.core.configuration.XmlConfigParser; import etm.core.monitor.EtmException; import etm.core.monitor.EtmMonitor; import etm.core.util.Log; import etm.core.util.LogAdapter; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AfterDeploymentValidation; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.BeforeBeanDiscovery; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; import javax.enterprise.util.AnnotationLiteral; import java.io.InputStream; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; /** * A CDI extension that activates performance monitoring using basic or xml based ETM configurations. * Tries to locate a valid <i>jetm-config.xml</i> and uses {@link etm.core.configuration.BasicEtmConfigurator} * as fallback. * <p/> * This extension delays auto start to <i>AfterDeploymentValidationPhase</i>. * * @author void.fm * @version $Revision$ * @since 1.3.0 */ public class QualifiedEtmExtension implements Extension { private static final LogAdapter LOG = Log.getLog(EtmMonitor.class); private static final String DEFAULT_CONFIG_FILE = "jetm-config.xml"; private EtmMonitorConfig monitorConfig; private boolean delayedAutoStart; private EtmMonitor etmMonitor; private ApplyToResolver resolver; public void beforeScan(@Observes BeforeBeanDiscovery event) { InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(DEFAULT_CONFIG_FILE); try { if (in != null) { monitorConfig = XmlConfigParser.extractConfig(in); delayedAutoStart = monitorConfig.isAutostart(); monitorConfig.setAutostart(false); } else { delayedAutoStart = true; } } catch (Exception e) { throw new EtmException(e); } resolver = new ApplyToResolver(); } public void afterScan(@Observes AfterBeanDiscovery event, BeanManager bm) { try { if (monitorConfig != null) { etmMonitor = EtmMonitorFactory.createEtmMonitor(monitorConfig); CdiEtmManager.configure(etmMonitor); } else { BasicEtmConfigurator.configure(true); etmMonitor = EtmManager.getEtmMonitor(); } } catch (Exception e) { event.addDefinitionError(e); return; } event.addBean(new EtmMonitorBean(bm, etmMonitor)); } public void afterDeployment(@Observes AfterDeploymentValidation event) { if (delayedAutoStart) { etmMonitor.start(); } } public <T> void addMeasurement(@Observes ProcessAnnotatedType<T> event) { AnnotatedType<T> annotatedType = event.getAnnotatedType(); if (!annotatedType.isAnnotationPresent(Measure.class)) { AnnotationLiteral<Measure> annotationLiteral = new AnnotationLiteral<Measure>() { }; if (resolver.isQualifiedApiType(annotatedType)) { LOG.debug("Adding public API performance monitoring to " + annotatedType.getJavaClass()); event.setAnnotatedType(new DelegatingAnnotatedType<T>(annotatedType, annotationLiteral)); } else if (resolver.isQualifiedMethodType(annotatedType)) { LOG.debug("Adding public method performance monitoring to " + annotatedType.getJavaClass()); event.setAnnotatedType(new PublicMethodApplyToDelegatingAnnotatedType<T>(annotatedType)); } } } /** * @author void.fm * @version $Revision$ * @since 1.3.0 */ static class EtmMonitorBean extends AbstractCdiBean<EtmMonitor> { private EtmMonitor etmMonitor; public EtmMonitorBean(BeanManager beanManager, EtmMonitor aEtmMonitor) { super(null, EtmMonitor.class, beanManager); etmMonitor = aEtmMonitor; } public EtmMonitor create(CreationalContext<EtmMonitor> context) { return etmMonitor; } public void destroy(EtmMonitor instance, CreationalContext<EtmMonitor> context) { instance.stop(); } @Override public Class<? extends Annotation> getScope() { return ApplicationScoped.class; } } /** * @author void.fm * @version $Revision$ * @since 1.3.0 */ static class CdiEtmManager extends EtmManager { /** * Sets the EtmMonitor. * * @param aEtmMonitor The new EtmMonitor which will be returned by {#getEtmMonitor}. */ protected static void configure(EtmMonitor aEtmMonitor) { EtmManager.configure(aEtmMonitor); } } static class ApplyToResolver { private Map<String, ApplyTo> cache = new HashMap<String, ApplyTo>(); ApplyToResolver() { // preload packages Package[] packages = Package.getPackages(); for (Package pkg : packages) { registerApplyToIfAppropriate(pkg.getName(), pkg.getAnnotation(ApplyTo.class)); } } protected <T> boolean isQualifiedApiType(AnnotatedType<T> type) { ApplyTo applyTo = findApplyTo(type.getJavaClass().getPackage().getName()); if (applyTo != null) { for (Class<? extends Annotation> t : applyTo.qualifiedApi()) { if (type.isAnnotationPresent(t)) { return true; } } } return false; } protected <T> boolean isQualifiedMethodType(AnnotatedType<T> type) { ApplyTo applyTo = findApplyTo(type.getJavaClass().getPackage().getName()); if (applyTo != null) { for (Class<? extends Annotation> t : applyTo.qualifiedMethod()) { if (type.isAnnotationPresent(t)) { return true; } } } return false; } protected <T> ApplyTo findApplyTo(String packageName) { while (packageName != null) { ApplyTo applyTo = cache.get(packageName); if (applyTo != null) { return applyTo; } else { try { Class<?> aClass = Class.forName(packageName + ".package-info"); ApplyTo annotation = aClass.getAnnotation(ApplyTo.class); registerApplyToIfAppropriate(packageName, annotation); if (annotation != null) { return annotation; } } catch (ClassNotFoundException e) { // ignored } } packageName = getParentPackage(packageName); } return null; } protected boolean registerApplyToIfAppropriate(String packageName, ApplyTo annotation) { if (annotation != null) { LOG.info("Using " + annotation + " for " + packageName + " and above."); cache.put(packageName, annotation); Package[] packages = Package.getPackages(); for (Package pkg : packages) { String name = pkg.getName(); if (!name.equals(packageName) && name.startsWith(packageName) && !cache.containsKey(name)) { if (pkg.isAnnotationPresent(ApplyTo.class)) { registerApplyToIfAppropriate(name, pkg.getAnnotation(ApplyTo.class)); } else { cache.put(name, annotation); } } } return true; } return false; } protected String getParentPackage(String aPackage) { int i = aPackage.lastIndexOf('.'); if (i >= 0) { return aPackage.substring(0, i); } return null; } } static class PublicMethodApplyToDelegatingAnnotatedType<T> extends DelegatingAnnotatedType<T> { private boolean initalized = false; private Measure classLevelMeasurementAttribute; public PublicMethodApplyToDelegatingAnnotatedType(AnnotatedType<T> delegateType) { super(delegateType); } protected AnnotatedMethod<? super T> processAnnotatedMethod(AnnotatedMethod<? super T> method) { // TODO String name = method.getJavaMember().getName(); if (getClassLevelMeasurementAttribute() == null && !name.startsWith("get") && !name.startsWith("set") && !name.startsWith("is") && !method.isAnnotationPresent(Measure.class)) { LOG.debug("Public Method Monitoring enabled for " + method.getJavaMember()); return new DelegatingAnnotatedMethod(this, method, new AnnotationLiteral<Measure>() { }); } else { return method; } } public Measure getClassLevelMeasurementAttribute() { if (!initalized) { init(); } return classLevelMeasurementAttribute; } private void init() { classLevelMeasurementAttribute = getDelegate().getAnnotation(Measure.class); initalized = true; } } }