All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bull.javamelody.JavaMelodyAutoConfiguration Maven / Gradle / Ivy

/*
 * Copyright 2008-2019 by Emeric Vernat
 *
 *     This file is part of Java Melody.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bull.javamelody;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.Schedules;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * Spring Boot auto-configuration for JavaMelody.
 *
 * 

* This class is picked up by the Spring Boot auto-configuration mechanism and creates the beans required to set up JavaMelody. * Configuration values are injected using {@link JavaMelodyConfigurationProperties}. *

* *

* The auto-configured filter can be overridden by defining a custom {@link FilterRegistrationBean} with the name * "javamelody-registration" in the application context. *

* *

* The configuration is enabled for web applications by default. It is possible to opt-out of the auto-configuration by * setting the application configuration "javamelody.enabled" to the value "false". *

* * @author Georg Wittberger, Emeric Vernat * @since 1.64.0 */ @Configuration @EnableConfigurationProperties(JavaMelodyConfigurationProperties.class) @ConditionalOnWebApplication @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "enabled", matchIfMissing = true) public class JavaMelodyAutoConfiguration { /** * Name of the FilterRegistrationBean. */ public static final String REGISTRATION_BEAN_NAME = "javamelody-registration"; private final MonitoredWithAnnotationPointcut monitoredWithSpringAnnotationPointcut = new MonitoredWithAnnotationPointcut(); private final Pointcut asyncAnnotationPointcut = Pointcuts.union( new AnnotationMatchingPointcut(Async.class), new AnnotationMatchingPointcut(null, Async.class)); private final Pointcut scheduledAnnotationPointcut = Pointcuts.union( new AnnotationMatchingPointcut(null, Scheduled.class), new AnnotationMatchingPointcut(null, Schedules.class)); /** * Registers the JavaMelody {@link SessionListener}. * @param servletContext ServletContext * @return ServletListenerRegistrationBean */ @Bean public ServletListenerRegistrationBean monitoringSessionListener( ServletContext servletContext) { final ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean<>( new SessionListener()); if (servletContext.getFilterRegistration("javamelody") != null) { // if webapp deployed as war in a container with MonitoringFilter and SessionListener already added by web-fragment.xml, // do not add again servletListenerRegistrationBean.setEnabled(false); } return servletListenerRegistrationBean; } /** * Registers the JavaMelody {@link MonitoringFilter}. The filter can be overridden completely by creating a custom * {@link FilterRegistrationBean} with the name "javamelody-registration" in the application context. * @param properties JavaMelodyConfigurationProperties * @param servletContext ServletContext * @return FilterRegistrationBean */ @Bean(name = REGISTRATION_BEAN_NAME) @ConditionalOnMissingBean(name = REGISTRATION_BEAN_NAME) public FilterRegistrationBean monitoringFilter( JavaMelodyConfigurationProperties properties, ServletContext servletContext) { final FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); // Create the monitoring filter and set its configuration parameters. final MonitoringFilter filter; if (properties.isManagementEndpointMonitoringEnabled()) { // if the management endpoint is enabled, disable the /monitoring reports on the application port filter = new MonitoringFilter() { @Override protected boolean isAllowed(HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden access"); return false; } }; } else { filter = new MonitoringFilter(); } filter.setApplicationType("Spring Boot"); // Wrap the monitoring filter in the registration bean. registrationBean.setFilter(filter); registrationBean.setAsyncSupported(true); registrationBean.setName("javamelody"); registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC); // Set the initialization parameter for the monitoring filter. for (final Entry parameter : properties.getInitParameters().entrySet()) { registrationBean.addInitParameter(parameter.getKey(), parameter.getValue()); } // Set the URL patterns to activate the monitoring filter for. registrationBean.addUrlPatterns("/*"); final FilterRegistration filterRegistration = servletContext .getFilterRegistration("javamelody"); if (filterRegistration != null) { // if webapp deployed as war in a container with MonitoringFilter already added by web-fragment.xml, // do not try to add it again registrationBean.setEnabled(false); for (final Map.Entry entry : registrationBean.getInitParameters() .entrySet()) { filterRegistration.setInitParameter(entry.getKey(), entry.getValue()); } } return registrationBean; } /** * When enabled, management endpoint for /monitoring reports on the management http port instead of the application http port. * @param servletContext ServletContext * @return MonitoringEndpoint */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "management-endpoint-monitoring-enabled", matchIfMissing = false) public MonitoringEndpoint monitoringEndpoint(final ServletContext servletContext) { return new MonitoringEndpoint(servletContext); } /** * Now disabled by default, since dependency spring-boot-starter-aop was added in 1.76. * @return DefaultAdvisorAutoProxyCreator */ @Bean @ConditionalOnMissingBean(DefaultAdvisorAutoProxyCreator.class) @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "advisor-auto-proxy-creator-enabled", matchIfMissing = false) public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { return new DefaultAdvisorAutoProxyCreator(); } /** * Monitoring of JDBC {@link DataSource}s * @param excludedDatasources Comma separated list of excluded datasources * @return SpringDataSourceBeanPostProcessor */ @Bean public SpringDataSourceBeanPostProcessor monitoringDataSourceBeanPostProcessor( @Value("${javamelody.excluded-datasources:}") String excludedDatasources) { // IMPORTANT: We cannot inject JavaMelodyConfigurationProperties here because of bean load order! Therefore we have // to use that rather dirty way to inject the configuration value. final SpringDataSourceBeanPostProcessor processor = new SpringDataSourceBeanPostProcessor(); if (excludedDatasources != null && excludedDatasources.trim().length() > 0) { processor.setExcludedDatasources( new HashSet<>(Arrays.asList(excludedDatasources.split(",")))); } return processor; } /** * Monitoring of beans and methods having the {@link MonitoredWithSpring} annotation. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public MonitoringSpringAdvisor monitoringSpringAdvisor() { return new MonitoringSpringAdvisor(monitoredWithSpringAnnotationPointcut); } /** * Monitoring of beans having the {@link Service} annotation. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public MonitoringSpringAdvisor monitoringSpringServiceAdvisor() { return createMonitoringSpringAdvisorWithExclusions( new AnnotationMatchingPointcut(Service.class), monitoredWithSpringAnnotationPointcut, asyncAnnotationPointcut, scheduledAnnotationPointcut); } /** * Monitoring of beans having the {@link Controller} annotation. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public MonitoringSpringAdvisor monitoringSpringControllerAdvisor() { return createMonitoringSpringAdvisorWithExclusions( new AnnotationMatchingPointcut(Controller.class), monitoredWithSpringAnnotationPointcut, asyncAnnotationPointcut, scheduledAnnotationPointcut); } /** * Monitoring of beans having the {@link RestController} annotation. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public MonitoringSpringAdvisor monitoringSpringRestControllerAdvisor() { return createMonitoringSpringAdvisorWithExclusions( new AnnotationMatchingPointcut(RestController.class), monitoredWithSpringAnnotationPointcut, asyncAnnotationPointcut, scheduledAnnotationPointcut); } /** * Monitoring of beans or methods having the {@link Async} annotation. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public MonitoringSpringAdvisor monitoringSpringAsyncAdvisor() { return createMonitoringSpringAdvisorWithExclusions(asyncAnnotationPointcut, monitoredWithSpringAnnotationPointcut, scheduledAnnotationPointcut); } /** * Monitoring of beans methods having the {@link Scheduled} or {@link Schedules} annotations. * @return MonitoringSpringAdvisor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "scheduled-monitoring-enabled", matchIfMissing = true) @ConditionalOnMissingBean(DefaultAdvisorAutoProxyCreator.class) public MonitoringSpringAdvisor monitoringSpringScheduledAdvisor() { return createMonitoringSpringAdvisorWithExclusions(scheduledAnnotationPointcut, monitoredWithSpringAnnotationPointcut, asyncAnnotationPointcut); } private MonitoringSpringAdvisor createMonitoringSpringAdvisorWithExclusions(Pointcut pointcut, Pointcut... excludedPointcuts) { final Pointcut myPointcut; if (excludedPointcuts.length == 0) { myPointcut = pointcut; } else { Pointcut excludedPointcut = excludedPointcuts[0]; if (excludedPointcuts.length > 1) { for (int i = 1; i < excludedPointcuts.length; i++) { excludedPointcut = Pointcuts.union(excludedPointcut, excludedPointcuts[i]); } } myPointcut = new ExcludingPointcut(pointcut).exclude(excludedPointcut); } return new MonitoringSpringAdvisor(myPointcut); } /** * Monitoring of Feign clients. * @return MonitoringSpringAdvisor * @throws ClassNotFoundException should not happen */ @SuppressWarnings("unchecked") @Bean @ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignClient") @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) // we check that the DefaultAdvisorAutoProxyCreator above is not enabled, because if it's enabled then feign calls are counted twice @ConditionalOnMissingBean(DefaultAdvisorAutoProxyCreator.class) public MonitoringSpringAdvisor monitoringFeignClientAdvisor() throws ClassNotFoundException { final Class feignClientClass = (Class) Class .forName("org.springframework.cloud.openfeign.FeignClient"); final MonitoringSpringAdvisor advisor = new MonitoringSpringAdvisor( new AnnotationMatchingPointcut(feignClientClass, true)); advisor.setAdvice(new MonitoringSpringInterceptor() { private static final long serialVersionUID = 1L; @Override protected String getRequestName(MethodInvocation invocation) { final StringBuilder sb = new StringBuilder(); final Method method = invocation.getMethod(); final RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); if (requestMapping != null) { String[] path = requestMapping.value(); if (path.length == 0) { path = requestMapping.path(); } if (path.length > 0) { sb.append(path[0]); sb.append(' '); if (requestMapping.method().length > 0) { sb.append(requestMapping.method()[0].name()); } else { sb.append("GET"); } sb.append('\n'); } } final Class declaringClass = method.getDeclaringClass(); final String classPart = declaringClass.getSimpleName(); final String methodPart = method.getName(); sb.append(classPart).append('.').append(methodPart); return sb.toString(); } }); return advisor; } /** * Monitoring of {@link RestTemplate} beans. * @return SpringRestTemplateBeanPostProcessor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public SpringRestTemplateBeanPostProcessor monitoringRestTemplateBeanPostProcessor() { return new SpringRestTemplateBeanPostProcessor(); } /** * Monitoring of MongoDbFactory beans. * @return SpringMongoDbFactoryBeanPostProcessor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public SpringMongoDbFactoryBeanPostProcessor monitoringMongoDbFactoryBeanPostProcessor() { return new SpringMongoDbFactoryBeanPostProcessor(); } /** * Monitoring of ElasticsearchOperations beans. * @return SpringElasticsearchOperationsBeanPostProcessor */ @Bean @ConditionalOnProperty(prefix = JavaMelodyConfigurationProperties.PREFIX, name = "spring-monitoring-enabled", matchIfMissing = true) public SpringElasticsearchOperationsBeanPostProcessor monitoringElasticsearchOperationsBeanPostProcessor() { return new SpringElasticsearchOperationsBeanPostProcessor(); } /** * Configure Spring's Schedulers for Quartz Scheduler * @return SchedulerFactoryBeanCustomizer */ @ConditionalOnClass(name = { "org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer", "org.springframework.scheduling.quartz.SchedulerFactoryBean", "org.quartz.JobListener" }) @Bean @ConditionalOnMissingBean public SchedulerFactoryBeanCustomizer schedulerFactoryBeanCustomizer() { return new SchedulerFactoryBeanCustomizer() { @Override public void customize(SchedulerFactoryBean schedulerFactoryBean) { final JobGlobalListener jobGlobalListener = new JobGlobalListener(); schedulerFactoryBean.setGlobalJobListeners(jobGlobalListener); schedulerFactoryBean.setExposeSchedulerInRepository(true); } }; } /** * @return Enregistrement du context Spring. */ @Bean public SpringContext javamelodySpringContext() { return new SpringContext(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy