net.bull.javamelody.SpringDataSourceBeanPostProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javamelody-core Show documentation
Show all versions of javamelody-core Show documentation
Monitoring of JavaEE applications
/*
* Copyright 2008-2017 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.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.jndi.JndiObjectFactoryBean;
import net.bull.javamelody.internal.common.LOG;
import net.bull.javamelody.internal.common.Parameters;
/**
* Post-processor Spring pour une éventuelle {@link DataSource} défini dans le fichier xml Spring.
* @author Emeric Vernat
*/
public class SpringDataSourceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
private Set excludedDatasources;
// l'interface PriorityOrdered place la priorité assez haute dans le contexte Spring
// quelle que soit la valeur de order
private int order = LOWEST_PRECEDENCE;
private final Class> delegatingDataSourceClass = getDelegatingDataSourceClass();
/**
* Définit les noms des datasources Spring exclues.
* @param excludedDatasources Set
*/
public void setExcludedDatasources(Set excludedDatasources) {
this.excludedDatasources = excludedDatasources;
// exemple:
//
//
//
// excludedDataSourceName
//
//
//
}
/** {@inheritDoc} */
@Override
public int getOrder() {
return order;
}
/**
* Définit la priorité dans le contexte Spring.
* @param order int
*/
public void setOrder(int order) {
this.order = order;
}
/** {@inheritDoc} */
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
private boolean isExcludedDataSource(String beanName) {
if (excludedDatasources != null && excludedDatasources.contains(beanName)) {
LOG.debug("Spring datasource excluded: " + beanName);
return true;
}
return false;
}
/** {@inheritDoc} */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof DataSource) {
// on ne teste isExcludedDataSource que si on est sur une datasource
if (isExcludedDataSource(beanName) || Parameters.isNoDatabase()
|| isDelegatingDataSourceAndAlreadyProxied(bean, beanName)) {
return bean;
}
final DataSource dataSource = (DataSource) bean;
JdbcWrapper.registerSpringDataSource(beanName, dataSource);
final DataSource result = JdbcWrapper.SINGLETON.createDataSourceProxy(beanName,
dataSource);
LOG.debug("Spring datasource wrapped: " + beanName);
return result;
} else if (bean instanceof JndiObjectFactoryBean) {
// ou sur un JndiObjectFactoryBean
if (isExcludedDataSource(beanName) || Parameters.isNoDatabase()) {
return bean;
}
// fix issue 20
final Object result = createProxy(bean, beanName);
LOG.debug("Spring JNDI factory wrapped: " + beanName);
return result;
}
// I tried here in the post-processor to fix "quartz jobs which are scheduled with spring
// are not displayed in javamelody, except if there is the following property for
// SchedulerFactoryBean in spring xml:
// ",
// but I had some problem with Spring creating the scheduler
// twice and so registering the scheduler in SchedulerRepository with the same name
// as the one registered below (and Quartz wants not)
// else if (bean != null
// && "org.springframework.scheduling.quartz.SchedulerFactoryBean".equals(bean
// .getClass().getName())) {
// try {
// // Remarque: on ajoute nous même le scheduler de Spring dans le SchedulerRepository
// // de Quartz, car l'appel ici de schedulerFactoryBean.setExposeSchedulerInRepository(true)
// // est trop tard et ne fonctionnerait pas
// final Method method = bean.getClass().getMethod("getScheduler", (Class>[]) null);
// final Scheduler scheduler = (Scheduler) method.invoke(bean, (Object[]) null);
//
// final SchedulerRepository schedulerRepository = SchedulerRepository.getInstance();
// synchronized (schedulerRepository) {
// if (schedulerRepository.lookup(scheduler.getSchedulerName()) == null) {
// schedulerRepository.bind(scheduler);
// scheduler.addGlobalJobListener(new JobGlobalListener());
// }
// }
// } catch (final NoSuchMethodException e) {
// // si la méthode n'existe pas (avant spring 2.5.6), alors cela marche sans rien faire
// return bean;
// } catch (final InvocationTargetException e) {
// // tant pis
// return bean;
// } catch (final IllegalAccessException e) {
// // tant pis
// return bean;
// } catch (SchedulerException e) {
// // tant pis
// return bean;
// }
// }
return bean;
}
private Object createProxy(final Object bean, final String beanName) {
final InvocationHandler invocationHandler = new InvocationHandler() {
/** {@inheritDoc} */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(bean, args);
if (result instanceof DataSource) {
result = JdbcWrapper.SINGLETON.createDataSourceProxy(beanName,
(DataSource) result);
}
return result;
}
};
return JdbcWrapper.createProxy(bean, invocationHandler);
}
private boolean isDelegatingDataSourceAndAlreadyProxied(Object bean, String beanName) {
// bean instanceof DelegatingDataSource ?
// use reflection in case spring-jdbc is not available
if (delegatingDataSourceClass != null && delegatingDataSourceClass.isInstance(bean)) {
final DataSource targetDataSource;
try {
targetDataSource = (DataSource) delegatingDataSourceClass
.getMethod("getTargetDataSource").invoke(bean);
} catch (final Exception e) {
// call to ((DelegatingDataSource) bean).getTargetDataSource() is not supposed to fail
throw new IllegalStateException(e);
}
if (JdbcWrapper.isProxyAlready(targetDataSource)) {
LOG.debug("Spring delegating datasource excluded: " + beanName);
return true;
}
}
return false;
}
private static Class> getDelegatingDataSourceClass() {
try {
return Class.forName("org.springframework.jdbc.datasource.DelegatingDataSource");
} catch (ClassNotFoundException e) {
return null;
}
}
}