package liquibase.resource;
import liquibase.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
/**
* A FileOpener that will search in a List of other FileOpeners until it finds
* one that has a resource of the appropriate name and path.
*
* @author <a href="mailto:csuml@yahoo.co.uk>Paul Keeble</a>
*/
public class CompositeResourceAccessor implements ResourceAccessor {
List<ResourceAccessor> openers;
/**
* Creates a Composite Opener with the list specified. The List will
* be searched in order from beginning to end.
*
* @param openers The list of Openers to use
*/
public CompositeResourceAccessor(List<ResourceAccessor> openers) {
this.openers = openers;
}
/**
* Creates a CompositeFileOpener with 2 entries.
*
* @param openers The list of Openers to use
*/
public CompositeResourceAccessor(ResourceAccessor... openers) {
this.openers = Arrays.asList(openers);
}
/**
* Searches through all of the FileOpeners in order for the file.
* <p/>
* If none of the FileOpeners was able to produce a stream to the file
* then null is returned.
*/
public InputStream getResourceAsStream(String file) throws IOException {
for (ResourceAccessor o : openers) {
InputStream is = o.getResourceAsStream(file);
if (is != null)
return is;
}
return null;
}
/**
* Searches all of the FileOpeners for a directory named packageName. If no
* results are found within any of the directories then an empty
* Enumeration is returned.
*/
public Enumeration<URL> getResources(String packageName) throws IOException {
Vector<URL> urls = new Vector<URL>();
for (ResourceAccessor o : openers) {
Enumeration<URL> e = o.getResources(packageName);
while (e.hasMoreElements()) {
urls.add(e.nextElement());
}
if (!urls.isEmpty())
break;
}
return urls.elements();
}
public ClassLoader toClassLoader() {
ClassLoader[] loaders=new ClassLoader[openers.size()];
int i=0;
for (ResourceAccessor fo: openers) {
loaders[i++]=fo.toClassLoader();
}
return new CompositeClassLoader(loaders);
}
//based on code from http://fisheye.codehaus.org/browse/xstream/trunk/xstream/src/java/com/thoughtworks/xstream/core/util/CompositeClassLoader.java?r=root
private static class CompositeClassLoader extends ClassLoader {
private final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
public CompositeClassLoader(ClassLoader... classLoaders) {
this.classLoaders.addAll(Arrays.asList(classLoaders));
}
@Override
public Class loadClass(String name,boolean resolve) throws ClassNotFoundException {
for (Object classLoader1 : classLoaders) {
ClassLoader classLoader = (ClassLoader) classLoader1;
try {
Class classe=classLoader.loadClass(name);
if(resolve)
resolveClass(classe);
return classe;
} catch (ClassNotFoundException notFound) {
// ok.. try another one
}
}
// One last try - the context class loader associated with the current thread. Often used in j2ee servers.
// Note: The contextClassLoader cannot be added to the classLoaders list up front as the thread that constructs
// liquibase is potentially different to thread that uses it.
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader != null) {
Class classe=contextClassLoader.loadClass(name);
if(resolve)
resolveClass(classe);
return classe;
} else {
throw new ClassNotFoundException(name);
}
}
}
@Override
public String toString() {
List<String> openerStrings = new ArrayList<String>();
for (ResourceAccessor opener : openers ) {
openerStrings.add(opener.toString());
}
return getClass().getName()+"("+StringUtils.join(openerStrings,",")+")";
}
}