net.bull.javamelody.JpaPersistence 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
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.LoadState;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceProviderResolver;
import javax.persistence.spi.PersistenceProviderResolverHolder;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.ProviderUtil;
import net.bull.javamelody.internal.common.LOG;
import net.bull.javamelody.internal.common.Parameters;
import net.bull.javamelody.internal.model.Counter;
/**
* Persistence provider pour monitorer JPA.
* From Sirona, http://sirona.incubator.apache.org/
*/
public class JpaPersistence implements PersistenceProvider {
private static final Counter JPA_COUNTER = MonitoringProxy.getJpaCounter();
private static final boolean COUNTER_HIDDEN = Parameters.isCounterHidden(JPA_COUNTER.getName());
/**
* The name of the {@link javax.persistence.spi.PersistenceProvider} implementor
*
* See JPA 2 sections 9.4.3 and 8.2.1.4
*/
private static final String JPA_PERSISTENCE_PROVIDER = "javax.persistence.provider";
private static final String OWN_PACKAGE = JpaPersistence.class.getName().substring(0,
JpaPersistence.class.getName().lastIndexOf('.'));
private static final String DELEGATE_PROVIDER_KEY = OWN_PACKAGE + ".jpa.provider";
private static final String[] PROVIDERS = {
"org.apache.openjpa.persistence.PersistenceProviderImpl",
"org.hibernate.jpa.HibernatePersistenceProvider",
"org.hibernate.ejb.HibernatePersistence",
"org.eclipse.persistence.jpa.PersistenceProvider",
"oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider",
"oracle.toplink.essentials.PersistenceProvider",
"me.prettyprint.hom.CassandraPersistenceProvider",
"org.datanucleus.jpa.PersistenceProviderImpl",
"com.orientechnologies.orient.core.db.object.jpa.OJPAPersistenceProvider",
"com.orientechnologies.orient.object.jpa.OJPAPersistenceProvider",
"com.spaceprogram.simplejpa.PersistenceProviderImpl", };
private static final ProviderUtil DUMMY_PROVIDER_UTIL = new ProviderUtil() {
@Override
public LoadState isLoadedWithoutReference(Object entity, String attributeName) {
return LoadState.UNKNOWN;
}
@Override
public LoadState isLoadedWithReference(Object entity, String attributeName) {
return LoadState.UNKNOWN;
}
@Override
public LoadState isLoaded(Object entity) {
return LoadState.UNKNOWN;
}
};
private volatile PersistenceProvider delegate; // NOPMD
private static class JavaMelodyPersistenceProviderResolver
implements PersistenceProviderResolver {
private final PersistenceProviderResolver delegate;
JavaMelodyPersistenceProviderResolver(PersistenceProviderResolver delegate) {
super();
this.delegate = delegate;
}
@Override
public List getPersistenceProviders() {
// avant de retourner la liste des persistence providers
// on met notre JpaPersistence en premier pour qu'il soit toujours choisi
// et qu'il délègue au persistence provider final
final List providers = delegate.getPersistenceProviders();
final List result = new ArrayList<>();
for (final PersistenceProvider provider : providers) {
if (provider instanceof JpaPersistence) {
result.add(0, provider);
} else {
result.add(provider);
}
}
return result;
}
@Override
public void clearCachedProviders() {
delegate.clearCachedProviders();
}
}
/**
* Active le monitoring JPA par défaut,
* même si net.bull.javamelody.JpaPersistence n'est pas dans META-INF/persistence.xml
*/
public static void initPersistenceProviderResolver() {
try {
PersistenceProviderResolver resolver = PersistenceProviderResolverHolder
.getPersistenceProviderResolver();
if (!(resolver instanceof JavaMelodyPersistenceProviderResolver)) {
resolver = new JavaMelodyPersistenceProviderResolver(resolver);
PersistenceProviderResolverHolder.setPersistenceProviderResolver(resolver);
LOG.debug("JPA persistence provider resolver initialized");
}
} catch (final Throwable t) { // NOPMD
LOG.info("initialization of jpa persistence provider resolver failed, skipping");
}
}
// cette classe est instanciée dès le démarrage (WildFly notamment),
// il ne faut donc pas appeler initJpaCounter() dans le constructeur
private void initJpaCounter() {
// quand cette classe est utilisée, le compteur est affiché
// sauf si le paramètre displayed-counters dit le contraire
JPA_COUNTER.setDisplayed(!COUNTER_HIDDEN);
// setUsed(true) nécessaire ici si le contexte jpa est initialisé avant FilterContext
// sinon les statistiques jpa ne sont pas affichées
JPA_COUNTER.setUsed(true);
LOG.debug("jpa persistence initialized");
}
/** {@inheritDoc} */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public EntityManagerFactory createEntityManagerFactory(final String unit, final Map map) {
initJpaCounter();
final PersistenceProvider persistenceProvider = findDelegate(map);
final ClassLoader tccl = tccl();
final ClassLoader hack = AccessController.doPrivileged(new PrivilegedAction() { // pour findbugs
/** {@inheritDoc} */
@Override
public ClassLoader run() {
return new JpaOverridePersistenceXmlClassLoader(tccl,
persistenceProvider.getClass().getName());
}
});
Thread.currentThread().setContextClassLoader(hack);
try {
final Map overridenMap = new HashMap();
if (map != null) {
overridenMap.putAll(map);
}
// #869 No Persistence provider for EntityManager, with Hibernate 5.4 & JPA
// (when JpaOverridePersistenceXmlClassLoader is not enough)
overridenMap.put(JPA_PERSISTENCE_PROVIDER, persistenceProvider.getClass().getName());
final EntityManagerFactory entityManagerFactory = persistenceProvider
.createEntityManagerFactory(unit, overridenMap);
if (entityManagerFactory == null) {
return null;
}
return JpaWrapper.createEntityManagerFactoryProxy(entityManagerFactory);
} finally {
Thread.currentThread().setContextClassLoader(tccl);
}
}
/** {@inheritDoc} */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public EntityManagerFactory createContainerEntityManagerFactory(final PersistenceUnitInfo info,
final Map map) {
initJpaCounter();
final PersistenceProvider persistenceProvider = findDelegate(map);
// on surcharge PersistenceUnitInfo.getPersistenceProviderClassName()
// pour retourner le PersistenceProvider délégué et pas nous même
final PersistenceUnitInfo proxiedInfo = createPersistentUnitInfoProxy(info,
persistenceProvider);
final Map overridenMap = new HashMap();
if (map != null) {
overridenMap.putAll(map);
}
// #869 No Persistence provider for EntityManager, with Hibernate 5.4 & JPA
// (when JpaOverridePersistenceXmlClassLoader is not enough)
overridenMap.put(JPA_PERSISTENCE_PROVIDER, persistenceProvider.getClass().getName());
final EntityManagerFactory entityManagerFactory = persistenceProvider
.createContainerEntityManagerFactory(proxiedInfo, overridenMap);
if (entityManagerFactory == null) {
return null;
}
return JpaWrapper.createEntityManagerFactoryProxy(entityManagerFactory);
}
private PersistenceUnitInfo createPersistentUnitInfoProxy(final PersistenceUnitInfo info,
final PersistenceProvider persistenceProvider) {
final InvocationHandler invocationHandler = new ProviderAwareHandler(
persistenceProvider.getClass().getName(), info);
return JdbcWrapper.createProxy(info, invocationHandler);
}
/** {@inheritDoc} */
@Override
public ProviderUtil getProviderUtil() { // we suppose it is loaded later than createXXXEMF so we'll get the delegate
if (delegate == null) {
// delegate not yet loaded and perhaps will never be:
// this method may be called, even without jpa impl to delegate,
// for example, by hibernate validator via jpa api but without jpa impl
// (issue 396, if loadOrGuessDelegate(null) was called here),
// so return a dumb ProviderUtil in this case or if delegate not yet loaded
return DUMMY_PROVIDER_UTIL;
}
return delegate.getProviderUtil();
}
private PersistenceProvider findDelegate(final Map, ?> map) {
if (map == null) {
return loadOrGuessDelegate(null);
}
return loadOrGuessDelegate(String.class.cast(map.get(DELEGATE_PROVIDER_KEY)));
}
private PersistenceProvider loadOrGuessDelegate(final String name) {
if (delegate == null) {
synchronized (this) {
if (delegate == null) {
if (name == null) {
guessDelegate();
} else {
try {
delegate = newPersistence(name);
} catch (final Exception e) {
throw new IllegalStateException(new ClassNotFoundException(
"Can't instantiate '" + name + "'", e));
}
}
}
}
}
if (name != null && !delegate.getClass().getName().equals(name)) {
try {
return newPersistence(name);
} catch (final Exception e) {
throw new IllegalStateException(
new ClassNotFoundException("Can't instantiate '" + name + "'", e));
}
}
return delegate;
}
private void guessDelegate() {
// https://issues.apache.org/jira/browse/SIRONA-44
// https://github.com/javamelody/javamelody/issues/460
final List persistenceProviders = PersistenceProviderResolverHolder
.getPersistenceProviderResolver().getPersistenceProviders();
for (final PersistenceProvider persistenceProvider : persistenceProviders) {
if (!getClass().isInstance(persistenceProvider)) {
delegate = persistenceProvider;
break;
}
}
if (delegate == null) {
for (final String provider : PROVIDERS) {
try {
delegate = newPersistence(provider);
break;
} catch (final Throwable th2) { // NOPMD
continue;
}
}
if (delegate == null) {
throw new IllegalStateException(
new ClassNotFoundException("Can't find a delegate"));
}
}
}
private static ClassLoader tccl() {
return Thread.currentThread().getContextClassLoader();
}
private static PersistenceProvider newPersistence(final String name) throws Exception { // NOPMD
return PersistenceProvider.class.cast(tccl().loadClass(name).newInstance());
}
private static class ProviderAwareHandler implements InvocationHandler {
private final String provider;
private final PersistenceUnitInfo info;
ProviderAwareHandler(final String provider, final PersistenceUnitInfo info) {
super();
this.provider = provider;
this.info = info;
}
/** {@inheritDoc} */
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
if ("getPersistenceProviderClassName".equals(method.getName())) {
return provider;
}
return method.invoke(info, args);
}
}
/** {@inheritDoc} */
@SuppressWarnings("rawtypes")
@Override
public void generateSchema(final PersistenceUnitInfo info, final Map map) {
final PersistenceProvider persistenceProvider = findDelegate(map);
persistenceProvider.generateSchema(info, map);
}
/** {@inheritDoc} */
@SuppressWarnings("rawtypes")
@Override
public boolean generateSchema(final String persistenceUnitName, final Map map) {
final PersistenceProvider persistenceProvider = findDelegate(map);
return persistenceProvider.generateSchema(persistenceUnitName, map);
}
}