HHH-7994 Improve OsgiClassLoader performance

This commit is contained in:
Brett Meyer 2013-02-15 17:38:27 -05:00 committed by Brett Meyer
parent 1e5fdfc689
commit 7611a5055c
3 changed files with 136 additions and 35 deletions

View File

@ -917,10 +917,7 @@ public class Ejb3Configuration implements Serializable, Referenceable {
thread = Thread.currentThread();
contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader( overridenClassLoader );
builder.withApplicationClassLoader( overridenClassLoader );
builder.withEnvironmentClassLoader( overridenClassLoader );
builder.withHibernateClassLoader( overridenClassLoader );
builder.withResourceClassLoader( overridenClassLoader );
builder.with( overridenClassLoader );
}
try {

View File

@ -0,0 +1,94 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.osgi;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.osgi.framework.Bundle;
/**
* Integrates a Bundle, its key, and classes/resources that have been found
* through its ClassLoader. Primarily used to clear the OsgiClassLoader
* caches once the Bundle is deactivated.
*
* @author Brett Meyer
*/
public class CachedBundle {
private Bundle bundle;
private String key;
private List<String> classNames = new ArrayList<String>();
private List<String> resourceNames = new ArrayList<String>();
private List<String> resourceListNames = new ArrayList<String>();
public CachedBundle( Bundle bundle, String key ) {
this.bundle = bundle;
this.key = key;
}
public Class loadClass(String name) throws ClassNotFoundException {
Class clazz = bundle.loadClass( name );
if ( clazz != null ) {
classNames.add( name );
}
return clazz;
}
public URL getResource(String name) {
URL resource = bundle.getResource( name );
if ( resource != null ) {
resourceNames.add( name );
}
return resource;
}
public Enumeration getResources(String name) throws IOException {
Enumeration resourceList = bundle.getResources( name );
if ( resourceList != null ) {
resourceListNames.add( name );
}
return resourceList;
}
public String getKey() {
return key;
}
public List<String> getClassNames() {
return classNames;
}
public List<String> getResourceNames() {
return resourceNames;
}
public List<String> getResourceListNames() {
return resourceListNames;
}
}

View File

@ -26,6 +26,8 @@ package org.hibernate.osgi;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.framework.Bundle;
@ -37,11 +39,13 @@ import org.osgi.framework.Bundle;
*/
public class OsgiClassLoader extends ClassLoader {
private HashMap<String, Bundle> bundles;
private Map<String, CachedBundle> bundles = new HashMap<String, CachedBundle>();
public OsgiClassLoader() {
bundles = new HashMap<String, Bundle>();
}
private Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
private Map<String, URL> resourceCache = new HashMap<String, URL>();
private Map<String, Enumeration<URL>> resourceListCache = new HashMap<String, Enumeration<URL>>();
/**
* Load the class and break on first found match.
@ -51,16 +55,15 @@ public class OsgiClassLoader extends ClassLoader {
@SuppressWarnings("rawtypes")
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO: This is horrible -- we shouldn't iterate over all the
// classloaders every time we need to construct an entity, etc. Instead,
// keep references to all classes/resources found in active bundles
// in memory? Find a way to identify what we "care about" and keep
// only those? Discover them the first time and then cache the
// reference?
for ( Bundle bundle : bundles.values() ) {
if ( classCache.containsKey( name ) ) {
return classCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
try {
Class clazz = bundle.loadClass( name );
if ( clazz != null ) {
classCache.put( name, clazz );
return clazz;
}
}
@ -78,16 +81,15 @@ public class OsgiClassLoader extends ClassLoader {
*/
@Override
protected URL findResource(String name) {
// TODO: This is horrible -- we shouldn't iterate over all the
// classloaders every time we need to construct an entity, etc. Instead,
// keep references to all classes/resources found in active bundles
// in memory? Find a way to identify what we "care about" and keep
// only those? Discover them the first time and then cache the
// reference?
for ( Bundle bundle : bundles.values() ) {
if ( resourceCache.containsKey( name ) ) {
return resourceCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
try {
URL resource = bundle.getResource( name );
if ( resource != null ) {
resourceCache.put( name, resource );
return resource;
}
}
@ -106,16 +108,15 @@ public class OsgiClassLoader extends ClassLoader {
@SuppressWarnings("unchecked")
@Override
protected Enumeration<URL> findResources(String name) {
// TODO: This is horrible -- we shouldn't iterate over all the
// classloaders every time we need to construct an entity, etc. Instead,
// keep references to all classes/resources found in active bundles
// in memory? Find a way to identify what we "care about" and keep
// only those? Discover them the first time and then cache the
// reference?
for ( Bundle bundle : bundles.values() ) {
if ( resourceListCache.containsKey( name ) ) {
return resourceListCache.get( name );
}
for ( CachedBundle bundle : bundles.values() ) {
try {
Enumeration<URL> resources = bundle.getResources( name );
if ( resources != null ) {
resourceListCache.put( name, resources );
return resources;
}
}
@ -132,11 +133,9 @@ public class OsgiClassLoader extends ClassLoader {
public void registerBundle(Bundle bundle) {
if ( bundle != null ) {
synchronized ( bundles ) {
// create a bundle classloader and add it to the list of
// classloaders
String key = getBundleKey( bundle );
if ( !bundles.containsKey( key ) ) {
bundles.put( key, bundle );
bundles.put( key, new CachedBundle( bundle, key ) );
}
}
}
@ -148,17 +147,28 @@ public class OsgiClassLoader extends ClassLoader {
public void unregisterBundle(Bundle bundle) {
if ( bundle != null ) {
synchronized ( bundles ) {
// remove a bundle classloader for a given bundle
String key = getBundleKey( bundle );
if ( bundles.containsKey( key ) ) {
bundles.remove( key );
CachedBundle cachedBundle = bundles.remove( key );
clearCache( classCache, cachedBundle.getClassNames() );
clearCache( resourceCache, cachedBundle.getResourceNames() );
clearCache( resourceListCache, cachedBundle.getResourceListNames() );
}
}
}
}
private void clearCache( Map cache, List<String> names ) {
for ( String name : names ) {
cache.remove( name );
}
}
public void clear() {
bundles.clear();
classCache.clear();
resourceCache.clear();
resourceListCache.clear();
}
protected static String getBundleKey(Bundle bundle) {