HHH-12553 - Prevent ConcurrentModificationException in OsgiClassLoader
This commit is contained in:
parent
fcdb89302a
commit
2bd99e5f32
|
@ -8,6 +8,7 @@ package org.hibernate.osgi;
|
|||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -15,6 +16,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.hibernate.service.spi.Stoppable;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
@ -22,19 +25,24 @@ import org.osgi.framework.Bundle;
|
|||
/**
|
||||
* Custom OSGI ClassLoader helper which knows all the "interesting"
|
||||
* class loaders and bundles. Encapsulates the OSGi related CL capabilities.
|
||||
*
|
||||
*
|
||||
* @author Brett Meyer
|
||||
* @author Tim Ward
|
||||
*/
|
||||
public class OsgiClassLoader extends ClassLoader implements Stoppable {
|
||||
// Leave these as Sets -- addClassLoader or addBundle may be called more
|
||||
// than once if a SF or EMF is closed and re-created.
|
||||
private Set<ClassLoader> classLoaders = new LinkedHashSet<ClassLoader>();
|
||||
private Set<Bundle> bundles = new LinkedHashSet<Bundle>();
|
||||
// HHH-12553: must be thread-safe. Concurrent impl. would be best, but we have to retain insertion-order.
|
||||
private Set<ClassLoader> classLoaders = Collections.synchronizedSet(new LinkedHashSet<ClassLoader>());
|
||||
private Set<Bundle> bundles = Collections.synchronizedSet(new LinkedHashSet<Bundle>());
|
||||
|
||||
private ConcurrentMap<String, Class<?>> classCache = new ConcurrentHashMap<String, Class<?>>();
|
||||
private ConcurrentMap<String, URL> resourceCache = new ConcurrentHashMap<String, URL>();
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
private Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
|
||||
private Map<String, URL> resourceCache = new HashMap<String, URL>();
|
||||
|
||||
public OsgiClassLoader() {
|
||||
// DO NOT use ClassLoader#parent, which is typically the SystemClassLoader for most containers. Instead,
|
||||
// allow the ClassNotFoundException to be thrown. ClassLoaderServiceImpl will check the SystemClassLoader
|
||||
|
@ -43,39 +51,44 @@ public class OsgiClassLoader extends ClassLoader implements Stoppable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Load the class and break on first found match.
|
||||
*
|
||||
* Load the class and break on first found match.
|
||||
*
|
||||
* TODO: Should this throw a different exception or warn if multiple
|
||||
* classes were found? Naming collisions can and do happen in OSGi...
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
if ( classCache.containsKey( name ) ) {
|
||||
return classCache.get( name );
|
||||
Class< ? > cachedClass = classCache.get( name );
|
||||
if ( cachedClass != null ) {
|
||||
return cachedClass;
|
||||
}
|
||||
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final Class clazz = bundle.loadClass( name );
|
||||
if ( clazz != null ) {
|
||||
classCache.put( name, clazz );
|
||||
return clazz;
|
||||
|
||||
synchronized (bundles) {
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final Class clazz = bundle.loadClass( name );
|
||||
if ( clazz != null ) {
|
||||
classCache.put( name, clazz );
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final Class clazz = classLoader.loadClass( name );
|
||||
if ( clazz != null ) {
|
||||
classCache.put( name, clazz );
|
||||
return clazz;
|
||||
|
||||
synchronized (classLoaders) {
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final Class clazz = classLoader.loadClass( name );
|
||||
if ( clazz != null ) {
|
||||
classCache.put( name, clazz );
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,49 +97,54 @@ public class OsgiClassLoader extends ClassLoader implements Stoppable {
|
|||
|
||||
/**
|
||||
* Load the class and break on first found match.
|
||||
*
|
||||
*
|
||||
* TODO: Should this throw a different exception or warn if multiple
|
||||
* classes were found? Naming collisions can and do happen in OSGi...
|
||||
*/
|
||||
@Override
|
||||
protected URL findResource(String name) {
|
||||
if ( resourceCache.containsKey( name ) ) {
|
||||
return resourceCache.get( name );
|
||||
URL cachedResource = resourceCache.get( name );
|
||||
if ( cachedResource != null ) {
|
||||
return cachedResource;
|
||||
}
|
||||
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final URL resource = bundle.getResource( name );
|
||||
if ( resource != null ) {
|
||||
resourceCache.put( name, resource );
|
||||
return resource;
|
||||
|
||||
synchronized (bundles) {
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final URL resource = bundle.getResource( name );
|
||||
if ( resource != null ) {
|
||||
resourceCache.put( name, resource );
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final URL resource = classLoader.getResource( name );
|
||||
if ( resource != null ) {
|
||||
resourceCache.put( name, resource );
|
||||
return resource;
|
||||
|
||||
synchronized (classLoaders) {
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final URL resource = classLoader.getResource( name );
|
||||
if ( resource != null ) {
|
||||
resourceCache.put( name, resource );
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Error?
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the class and break on first found match.
|
||||
*
|
||||
* Note: Since they're Enumerations, do not cache these results!
|
||||
*
|
||||
*
|
||||
* Note: Since they're Enumerations, do not cache these results!
|
||||
*
|
||||
* TODO: Should this throw a different exception or warn if multiple
|
||||
* classes were found? Naming collisions can and do happen in OSGi...
|
||||
*/
|
||||
|
@ -134,29 +152,33 @@ public class OsgiClassLoader extends ClassLoader implements Stoppable {
|
|||
@SuppressWarnings("unchecked")
|
||||
protected Enumeration<URL> findResources(String name) {
|
||||
final List<Enumeration<URL>> enumerations = new ArrayList<Enumeration<URL>>();
|
||||
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final Enumeration<URL> resources = bundle.getResources( name );
|
||||
if ( resources != null ) {
|
||||
enumerations.add( resources );
|
||||
|
||||
synchronized (bundles) {
|
||||
for ( Bundle bundle : bundles ) {
|
||||
try {
|
||||
final Enumeration<URL> resources = bundle.getResources( name );
|
||||
if ( resources != null ) {
|
||||
enumerations.add( resources );
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final Enumeration<URL> resources = classLoader.getResources( name );
|
||||
if ( resources != null ) {
|
||||
enumerations.add( resources );
|
||||
|
||||
synchronized (classLoaders) {
|
||||
for ( ClassLoader classLoader : classLoaders ) {
|
||||
try {
|
||||
final Enumeration<URL> resources = classLoader.getResources( name );
|
||||
if ( resources != null ) {
|
||||
enumerations.add( resources );
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
catch ( Exception ignore ) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Enumeration<URL> aggEnumeration = new Enumeration<URL>() {
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
|
@ -178,7 +200,7 @@ public class OsgiClassLoader extends ClassLoader implements Stoppable {
|
|||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return aggEnumeration;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue