Switched to Reflections (http://code.google.com/p/reflections/) project for

inspecting the source tree and loading walkers, rather than trying to roll
our own by hand.


git-svn-id: file:///humgen/gsa-scr1/gsa-engineering/svn_contents/trunk@1286 348d0f76-0448-11de-a6fe-93d51630548a
This commit is contained in:
hanna 2009-07-21 18:32:22 +00:00
parent 436a196e2b
commit b43925c01e
12 changed files with 60 additions and 319 deletions

View File

@ -10,5 +10,10 @@
<dependency org="jboss" name="javassist" rev="3.7.ga" />
<dependency org="org.simpleframework" name="simple-xml" rev="2.0.4" />
<dependency org="org.apache.bcel" name="bcel" rev="5.2" />
<!-- Dependencies for reflections mvn repository -->
<dependency org="org.reflections" name="reflections" rev="0.9.2" />
<dependency org="com.google.collections" name="google-collections" rev="0.9" />
</dependencies>
</ivy-module>

View File

@ -42,9 +42,6 @@ public class CommandLineGATK extends CommandLineExecutable {
@Argument(fullName = "analysis_type", shortName = "T", doc = "Type of analysis to run")
private String analysisName = null;
@Argument(fullName = "plugin_path", doc = "Which path will the GATK search for plugin walkers.", required = false)
private String pluginPath = null;
// our argument collection, the collection of command line args we accept
@ArgumentCollection
private GATKArgumentCollection argCollection = new GATKArgumentCollection();
@ -72,7 +69,7 @@ public class CommandLineGATK extends CommandLineExecutable {
@Override
protected GenomeAnalysisEngine getGATKEngine() {
if( GATKEngine == null )
GATKEngine = new GenomeAnalysisEngine( pluginPath );
GATKEngine = new GenomeAnalysisEngine();
return GATKEngine;
}

View File

@ -70,10 +70,10 @@ public class GenomeAnalysisEngine {
* new MicroScheduler class we'll be able to delete that function.
*
*/
public GenomeAnalysisEngine( String pluginPathName ) {
public GenomeAnalysisEngine() {
// make sure our instance variable points to this analysis engine
instance = this;
walkerManager = new WalkerManager(pluginPathName);
walkerManager = new WalkerManager();
}
/**

View File

@ -1,19 +1,12 @@
package org.broadinstitute.sting.gatk;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.*;
import org.broadinstitute.sting.gatk.walkers.*;
import org.broadinstitute.sting.gatk.refdata.ReferenceOrderedDatum;
import org.broadinstitute.sting.gatk.refdata.ReferenceOrderedData;
import org.broadinstitute.sting.utils.JVMUtils;
import org.broadinstitute.sting.utils.PathUtils;
import org.broadinstitute.sting.utils.StingException;
import org.broadinstitute.sting.utils.PackageUtils;
import org.apache.log4j.Logger;
import net.sf.picard.filter.SamRecordFilter;
@ -33,42 +26,9 @@ public class WalkerManager {
private Map<String, Class<? extends Walker>> walkersByName;
public WalkerManager(String pluginDirectory) {
try {
List<Class> walkerCandidates = new ArrayList<Class>();
// Load all classes that live in this jar.
final File location = JVMUtils.getLocationFor( getClass() );
walkerCandidates.addAll(loadClassesFromLocation(location));
// Load all classes that live in the extension path.
if (pluginDirectory == null)
pluginDirectory = location.getParent() + File.separator + "walkers";
logger.info("plugin directory: " + pluginDirectory);
File extensionPath = new File(pluginDirectory);
if (extensionPath.exists()) {
List<String> classFilesInPath = PathUtils.findFilesInPath(extensionPath, "", "class", false);
walkerCandidates.addAll(JVMUtils.loadExternalClasses(extensionPath, classFilesInPath));
List<String> jarsInPath = PathUtils.findFilesInPath(extensionPath, "", "jar", false);
for( String jarFileName: jarsInPath ) {
File jarFile = new File( extensionPath, jarFileName );
walkerCandidates.addAll(JVMUtils.loadExternalClassesFromJar(jarFile) );
}
}
List<Class<? extends Walker>> walkers = filterWalkers(walkerCandidates);
if (walkerCandidates.isEmpty())
throw new RuntimeException("No walkers were found.");
walkersByName = createWalkerDatabase(walkers);
}
// IOExceptions here are suspect; they indicate that the WalkerManager can't open its containing jar.
// Wrap in a RuntimeException.
catch (IOException ex) {
throw new RuntimeException(ex);
}
public WalkerManager() {
List<Class<? extends Walker>> walkers = PackageUtils.getClassesImplementingInterface(Walker.class);
walkersByName = createWalkerDatabase(walkers);
}
/**
@ -206,38 +166,6 @@ public class WalkerManager {
return filters;
}
/**
* Load classes internal to the classpath from an arbitrary location.
*
* @param location Location from which to load classes.
* @return List of classes.
* @throws IOException Problem occurred reading classes.
*/
private List<Class> loadClassesFromLocation(File location)
throws IOException {
if (location.getAbsolutePath().endsWith(".jar"))
return JVMUtils.loadInternalClassesFromJar(location);
else {
List<String> classFileNames = PathUtils.findFilesInPath(location, "", "class", true);
return JVMUtils.loadInternalClasses(classFileNames);
}
}
/**
* Given a list of classes, return a list of those classes which extend from the Walker base interface.
*
* @param classes Arbitrary list of classes.
* @return List of classes extending from Walker.
*/
private List<Class<? extends Walker>> filterWalkers(List<Class> classes) {
List<Class<? extends Walker>> walkerClasses = new ArrayList<Class<? extends Walker>>();
for( Class clazz: classes ) {
if( JVMUtils.isConcreteImplementationOf(clazz,Walker.class) )
walkerClasses.add( (Class<? extends Walker>)clazz );
}
return walkerClasses;
}
/**
* Instantiate the list of walker classes. Add them to the walker hashmap.
*

View File

@ -16,9 +16,7 @@ import org.broadinstitute.sting.playground.gatk.walkers.variants.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.*;
/**
* VariantFiltrationWalker applies specified conditionally independent features to pre-called variants, thus modifying
@ -33,8 +31,8 @@ public class VariantFiltrationWalker extends LocusWalker<Integer, Integer> {
@Argument(fullName="list", shortName="ls", doc="List the available features and exclusion criteria and exit") public Boolean LIST = false;
@Argument(fullName="truth", shortName="truth", doc="Operate on truth set only") public Boolean TRUTH = false;
private ArrayList<Class> featureClasses;
private ArrayList<Class> exclusionClasses;
private List<Class<? extends IndependentVariantFeature>> featureClasses;
private List<Class<? extends VariantExclusionCriterion>> exclusionClasses;
private PrintWriter vwriter;
private HashMap<String, PrintWriter> ewriters;
@ -143,7 +141,7 @@ public class VariantFiltrationWalker extends LocusWalker<Integer, Integer> {
* @param classes an ArrayList of classes
* @return String of available classes
*/
private String getAvailableClasses(ArrayList<Class> classes) {
private <T> String getAvailableClasses(List<Class<? extends T>> classes) {
String availableString = "";
for (int classIndex = 0; classIndex < classes.size(); classIndex++) {

View File

@ -63,7 +63,7 @@ public class SomaticCoverageTool extends CommandLineExecutable {
@Override
protected GenomeAnalysisEngine getGATKEngine() {
if( GATKEngine == null )
GATKEngine = new GenomeAnalysisEngine( null );
GATKEngine = new GenomeAnalysisEngine();
return GATKEngine;
}

View File

@ -1,14 +1,5 @@
package org.broadinstitute.sting.utils;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.jar.JarInputStream;
import java.util.jar.JarEntry;
import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Modifier;
/**
@ -26,189 +17,14 @@ public class JVMUtils {
*/
private JVMUtils() { }
/**
* Determines which location contains the specified class.
*
* @return Location (either jar file or directory) of path containing class.
*/
public static File getLocationFor( Class clazz ) throws IOException {
try {
java.net.URI locationURI = clazz.getProtectionDomain().getCodeSource().getLocation().toURI();
return new File(locationURI);
}
catch (java.net.URISyntaxException ex) {
// a URISyntaxException here must be an IO error; wrap as such.
throw new IOException(ex);
}
catch ( NullPointerException ne ) {
throw new IOException("Can not extract code source location for "+clazz.getName());
}
}
/**
* Loads concrete classes from a jar which are both in the same package or 'sub-package' of baseClass,
* and which extend from baseClass. Loaded classes must already be on the classpath.
*
* @param jarFile The jar file to search.
* @return A list of classes derived from baseClass.
*/
public static List<Class> loadInternalClassesFromJar(final File jarFile)
throws IOException {
return loadClassesFromJar( jarFile, new InternalLoadingStrategy() );
}
/**
* Loads concrete classes from a jar which are both in the same package or 'sub-package' of baseClass,
* and which extend from baseClass. Loaded classes can be outside of the current classpath.
*
* @param jarFile The jar file to search.
* @return A list of classes derived from baseClass.
*/
public static List<Class> loadExternalClassesFromJar(final File jarFile)
throws IOException {
return loadClassesFromJar( jarFile, new ExternalLoadingStrategy(jarFile) );
}
/**
* Loads a list of classes currently on the classpath.
*
* @param classFileNames List of files representing classes.
* @return class objects.
* @throws IOException Unable to open any of the found classes.
*/
public static List<Class> loadInternalClasses(List<String> classFileNames)
throws IOException {
return loadClasses( classFileNames, new InternalLoadingStrategy() );
}
/**
* Load loose classes, external to the classloader, from the specified directory.
*
* @param path source path from which to load classes.
* @return A list of all loose classes contained in the path directory.
*/
public static List<Class> loadExternalClasses(final File path, List<String> classFileNames)
throws IOException {
return loadClasses( classFileNames, new ExternalLoadingStrategy( path ) );
}
/**
* Convert a filename of the form a/b/c.class to a.b.c. Makes no assurances about whether the
* class is valid on any classloader.
*
* @param fileName Filename to convert.
* @return classname represented by that file.
*/
public static String fileNameToClassName(String fileName) {
return fileName.substring(0, fileName.lastIndexOf(".class")).replace('/', '.');
}
/**
* Is the specified class a concrete implementation of baseClass?
* @param clazz Class to check.
* @param baseClass Base class to check against.
* @return True if clazz implements baseClass and is not abstract / an interface. False otherwise.
* @return True if clazz is concrete. False otherwise.
*/
public static boolean isConcreteImplementationOf( Class clazz, Class baseClass ) {
return baseClass.isAssignableFrom(clazz) &&
!Modifier.isAbstract(clazz.getModifiers()) &&
!Modifier.isInterface(clazz.getModifiers());
}
/**
* Loads a list of classes from the given jar, using the provided loading strategy.
* @param jarFile Jar file from which to load.
* @param loader Dictates how these classes should be loaded.
* @return A list of loaded classes.
* @throws IOException In case there's an IO error trying to load the jar.
*/
private static List<Class> loadClassesFromJar( final File jarFile, final LoadingStrategy loader )
throws IOException {
List<Class> classes = new ArrayList<Class>();
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile));
try {
JarEntry jarEntry = jarInputStream.getNextJarEntry();
while (jarEntry != null) {
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {
String className = fileNameToClassName(jarEntryName);
classes.add( loader.load( className ) );
}
jarEntry = jarInputStream.getNextJarEntry();
}
}
catch (ClassNotFoundException ex) {
// A ClassNotFoundException here must be an IO error; wrap as such.
throw new IOException(ex);
}
finally {
jarInputStream.close();
}
return classes;
}
/**
* Loads a list of classes, using the provided loading strategy.
* @param classFileNames Which class files to load.
* @param loader Dictates how these classes should be loaded.
* @return A list of loaded classes.
* @throws IOException In case there's an IO error trying to load the jar.
*/
private static List<Class> loadClasses( List<String> classFileNames, LoadingStrategy loader )
throws IOException {
List<Class> classes = new ArrayList<Class>();
for (String classFileName : classFileNames) {
String className = fileNameToClassName(classFileName);
try {
classes.add( loader.load( className ) );
}
catch (ClassNotFoundException ex) {
// A ClassNotFoundException here must be an IO error; wrap as such.
throw new IOException(ex);
}
}
return classes;
}
/**
* What mechanism should we use for loading a list of classes?
*/
private static interface LoadingStrategy {
Class load( String className ) throws ClassNotFoundException;
}
/**
* An internal loading strategy, for loading classes already on the classpath.
*/
private static class InternalLoadingStrategy implements LoadingStrategy {
public Class load( String className )
throws ClassNotFoundException {
return Class.forName( className );
}
}
/**
* An external loading strategy, for loading classes not necessarily already
* on the classpath.
*/
private static class ExternalLoadingStrategy implements LoadingStrategy {
private final ClassLoader classLoader;
public ExternalLoadingStrategy( final File jarFile ) throws IOException {
URL pathURL = jarFile.toURI().toURL();
classLoader = new URLClassLoader(new URL[]{pathURL});
}
public Class load( String className ) throws ClassNotFoundException {
return classLoader.loadClass(className);
}
public static boolean isConcrete( Class clazz ) {
return !Modifier.isAbstract(clazz.getModifiers()) &&
!Modifier.isInterface(clazz.getModifiers());
}
}

View File

@ -1,56 +1,55 @@
package org.broadinstitute.sting.utils;
import java.util.List;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.AbstractConfiguration;
import org.reflections.util.ClasspathHelper;
import java.util.Set;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* PackageUtils contains some useful methods for package introspection.
*/
public class PackageUtils {
/**
* A reference into our introspection utility.
*/
private static Reflections reflections = null;
static {
// Initialize general-purpose source tree reflector.
reflections = new Reflections( new AbstractConfiguration() {
{
setUrls(ClasspathHelper.getUrlsForCurrentClasspath());
setScanners(new SubTypesScanner());
}
});
}
/**
* Private constructor. No instantiating this class!
*/
private PackageUtils() {}
{
}
/**
* Return the classes that implement the specified interface.
*
* @param iface the interface which returned classes should implement.
* @return the list of classes that implement the interface. How is that not clear by now?!!!!111one!!
* @return the list of classes that implement the interface.
*/
public static ArrayList<Class> getClassesImplementingInterface(Class iface) {
try {
final File location = JVMUtils.getLocationFor(iface);
List<Class> potentialClasses = getClassesFromLocation(location);
ArrayList<Class> implementingClasses = new ArrayList<Class>();
for (Class potentialClass : potentialClasses) {
if (JVMUtils.isConcreteImplementationOf(potentialClass, iface)) {
implementingClasses.add(potentialClass);
}
}
return implementingClasses;
} catch (IOException e) {
throw new StingException(String.format("Unable to inspect package containing '%s'", iface.getName()));
public static <T> List<Class<? extends T>> getClassesImplementingInterface(Class<T> iface) {
// Load all classes implementing the given interface, then filter out any class that isn't concrete.
Set<Class<? extends T>> allTypes = reflections.getSubTypesOf(iface);
List<Class<? extends T>> concreteTypes = new ArrayList<Class<? extends T>>();
for( Class<? extends T> type: allTypes ) {
if( JVMUtils.isConcrete(type) )
concreteTypes.add(type);
}
}
/**
* Return a list of classes at the specified location
* @param location the location where we should start searching (as returned by JVMUtils.getLocationFor(Class))
* @return a list of classes at this location
* @throws IOException thrown if the jar or directory cannot be inspected
*/
public static List<Class> getClassesFromLocation(File location) throws IOException {
if (location.getAbsolutePath().endsWith(".jar"))
return JVMUtils.loadInternalClassesFromJar(location);
else {
List<String> classFileNames = PathUtils.findFilesInPath(location, "", "class", true);
return JVMUtils.loadInternalClasses(classFileNames);
}
return concreteTypes;
}
}

View File

@ -99,13 +99,7 @@ public abstract class CommandLineProgram {
// Default implementation to find a command line that makes sense.
// If the user is running from a jar, return '-jar <jarname>'; otherwise
// return the full class name.
String runningInstructions = null;
try {
runningInstructions = JVMUtils.getLocationFor( getClass() ).getName();
}
catch( IOException ex ) {
throw new StingException("Unable to determine running instructions", ex);
}
String runningInstructions = getClass().getName();
if( runningInstructions.endsWith(".jar") )
runningInstructions = String.format("-jar %s", runningInstructions);

View File

@ -12,5 +12,6 @@
<module organisation="edu.mit.broad" resolver="projects" />
<module organisation="net.sf" module="functionalj" resolver="projects" />
<module organisation="net.sf" module="samtools" resolver="projects" />
<module organisation="org.reflections" module="reflections" resolver="projects" />
</modules>
</ivysettings>

View File

@ -0,0 +1,3 @@
<ivy-module version="1.0">
<info organisation="org.reflections" module="reflections" revision="0.9.2" status="release" />
</ivy-module>