ClassFinder

+ andere TechDocs
+


Um flexibel und dynamisch zur Laufzeit Klassen mit bestimmten Eigenschaften (z.B. bestimmter Package-Pfad oder bestimmtes implementiertes Interface) verwenden zu können, muss der Classpath und/oder müssen .jar-Libs durchsucht werden.
Das vorgestellte Beispiel kann entweder eine Liste der gefundenen Klassen oder wahlweise eine Liste instanziierter Objekte returnieren.



ClassFinder

  1. Erzeugen Sie folgende Projektverzeichnisstruktur:

    [MeinWorkspace]
     `- [ClassFinder]
         |- [bin]
         |- [lib]
         `- [src]
             `- [classfinder]
    
  2. Speichern Sie im Verzeichnis <Projektverzeichnis>\src\classfinder die folgende Klasse ClassFinder.java:

    package classfinder;
    
    import java.io.File;
    import java.lang.reflect.Modifier;
    import java.util.*;
    import java.util.zip.*;
    
    /**
     * Test-Aufruf-Beispiele (der letzte Aufruf mit junit-...jar im Classpath):
     *   java classfinder.ClassFinder
     *   java classfinder.ClassFinder classfinder
     *   java classfinder.ClassFinder "" classfinder.MeinInterface
     *   java classfinder.ClassFinder "" classfinder.MeineAbstrakteKlasse
     *   java classfinder.ClassFinder "" org.junit.runner.Result
     */
    public class ClassFinder
    {
       // Die main()-Methode ist hauptsächlich für Tests:
       public static void main( String[] args ) throws Exception
       {
          String packageName       = ( args.length > 0 ) ? args[0] : null;
          String classNameSearched = ( args.length > 1 ) ? args[1] : null;
          System.out.println( "\n---- Gefundene Klassen:" );
          List<Class<?>> classes = getClasses( packageName, classNameSearched );
          for( Class<?> clazz : classes )
             System.out.println( clazz );
          System.out.println( "\n---- Instanziierte Objekte:" );
          List<Object> objects = getInstances( packageName, classNameSearched );
          for( Object obj : objects )
             System.out.println( obj.getClass() );
       }
    
       // Finde Klassen und instanziiere sie:
       public static List<Object> getInstances( String packageName, String classNameSearched ) throws ClassNotFoundException
       {
          List<Class<?>> classes = ClassFinder.getClasses( packageName, classNameSearched );
          List<Object>   objects = new ArrayList<Object>();
          for( Class<?> clazz : classes ) {
             if( !clazz.isInterface() && (clazz.getModifiers() & Modifier.ABSTRACT) == 0 ) {
                try {
                   objects.add( clazz.newInstance() );
                } catch( Exception ex ) {
                   // nur instanziierbare Klassen sind interessant
                }
             }
          }
          return objects;
       }
    
       // Finde Klassen (über Interface- oder Klassennamen bzw. Package-Namen):
       public static List<Class<?>> getClasses( String packageName, String classNameSearched ) throws ClassNotFoundException
       {
          Class<?> classSearched = ( classNameSearched != null ) ? Class.forName( classNameSearched ) : null;
          return getClasses( packageName, classSearched );
       }
    
       // Finde Klassen (über Interface oder Klasse bzw. Package-Namen):
       public static List<Class<?>> getClasses( String packageName, Class<?> classSearched )
       {
          List<Class<?>> classes = new ArrayList<Class<?>>();
          for( String path : getPathesFromClasspath() ) {
             File fileOrDir = new File( path );
             if( fileOrDir.isDirectory() )
                classes.addAll( getClassesFromDir( fileOrDir, packageName, classSearched ) );
             if( fileOrDir.isFile() && (fileOrDir.getName().toLowerCase().endsWith( ".jar" ) ||
                                        fileOrDir.getName().toLowerCase().endsWith( ".zip" )) )
                classes.addAll( getClassesFromJar( fileOrDir, packageName, classSearched ) );
          }
          return Collections.unmodifiableList( classes );
       }
    
       public static List<String> getPathesFromClasspath()
       {
          String          classpath     = System.getProperty( "java.class.path" );
          String          pathseparator = System.getProperty( "path.separator" );
          StringTokenizer tokenizer     = new StringTokenizer( classpath, pathseparator );
          List<String>    pathes        = new ArrayList<String>();
          while( tokenizer.hasMoreElements() )
             pathes.add(tokenizer.nextToken());
          return Collections.unmodifiableList( pathes );
       }
    
       public static List<Class<?>> getClassesFromJar( File file, String packageName, Class<?> classSearched )
       {
          if( packageName == null ) packageName = "";
          List<Class<?>> classes = new ArrayList<Class<?>>();
          String         dirSearched = packageName.replace( ".", "/" );
          ZipFile        zipFile = null;
          try {
             zipFile = new ZipFile( file );
          } catch( Exception ex ) {
             // nur Dateien, die gezippt sind und geöffnet werden können, sind interessant
             return classes;
          }
          for( Enumeration<? extends ZipEntry> zipEntries = zipFile.entries(); zipEntries.hasMoreElements(); )
          {
             String entryName = zipEntries.nextElement().getName();
             if( !entryName.startsWith( dirSearched ) ||
                 !entryName.toLowerCase().endsWith( ".class" ) )
                continue;
             entryName = entryName.substring( 0, entryName.length() - ".class".length() );
             entryName = entryName.replace( "/","." ) ;
             try {
                Class<?> clazz = Class.forName( entryName );
                if( classSearched == null || classSearched.isAssignableFrom( clazz ) )
                   classes.add( clazz );
             } catch( Throwable ex ) {
                // nur 'verwendbare' Klassen sind interessant
             }
          }
          try { zipFile.close(); } catch( Exception ex ) { /* wird ignoriert */ }
          return Collections.unmodifiableList( classes );
       }
    
       public static List<Class<?>> getClassesFromDir( File dir, String packageName, Class<?> classSearched )
       {
          if( packageName == null ) packageName = "";
          List<Class<?>> classes = new ArrayList<Class<?>>();
          File dirSearched = new File( dir.getPath() + File.separator + packageName.replace( ".", "/" ) );
          if( dirSearched.isDirectory() )
             getClassesFromFileOrDirIntern( true, dirSearched, packageName, classSearched, classes );
          return Collections.unmodifiableList( classes );
       }
    
       private static void getClassesFromFileOrDirIntern( boolean first, File fileOrDir, String packageName,
                                                          Class<?> classSearched, List<Class<?>> classes )
       {
          if( fileOrDir.isDirectory() )
          {
             if( !first )
                packageName = (packageName + "." + fileOrDir.getName()).replaceAll( "^\\.", "" );
             for( String subFileOrDir : fileOrDir.list() )
                getClassesFromFileOrDirIntern( false, new File( fileOrDir, subFileOrDir ),
                                               packageName, classSearched, classes );
          }
          else
          {
             if( fileOrDir.getName().toLowerCase().endsWith( ".class" ) )
             {
                String classFile = fileOrDir.getName();
                classFile = packageName + "." + classFile.substring( 0, classFile.length() - ".class".length() );
                try {
                   Class<?> clazz = Class.forName( classFile );
                   if( classSearched == null || classSearched.isAssignableFrom( clazz ) )
                      classes.add( clazz );
                } catch( Throwable ex ) {
                   // nur 'verwendbare' Klassen sind interessant
                }
             }
          }
       }
    }
    
  3. Speichern Sie für Tests im Verzeichnis <Projektverzeichnis>\src\classfinder die folgende Klasse TestKlassen.java (fügen sie darin oder zusätzlich weitere Klassen hinzu):

    package classfinder;
    
    public class TestKlassen
    {
    }
    
    interface MeinInterface
    {
       String execute();
    }
    
    class MeineKlasse1 implements MeinInterface
    {
       public String execute() { return this.getClass().getName(); }
    }
    
    abstract class MeineAbstrakteKlasse implements MeinInterface
    {
       public String execute() { return this.getClass().getName(); }
    }
    
    class MeineKlasse2 extends MeineAbstrakteKlasse {}
    
  4. Speichern Sie für Tests im lib-Verzeichnis eine beliebige .jar-Lib, zum Beispiel junit-...jar.
    Ihr Projektverzeichnis sieht jetzt so aus (überprüfen Sie es mit "tree /F"):

    [MeinWorkspace]
     `- [ClassFinder]
         |- [bin]
         |- [lib]
         |   `- ... [z.B. junit-...jar]
         `- [src]
             `- [classfinder]
                 |- ClassFinder.java
                 `- TestKlassen.java
    
  5. Öffnen Sie ein Kommandozeilenfenster ('Windows-Taste' + 'R', 'cmd') und führen Sie folgende Kommandos aus:

    cd \MeinWorkspace\ClassFinder

    tree /F

    javac -d bin -cp bin src\classfinder\*.java

    java -cp bin;lib/* classfinder.ClassFinder classfinder

    java -cp bin;lib/* classfinder.ClassFinder "" classfinder.MeinInterface

    java -cp bin;lib/* classfinder.ClassFinder "" classfinder.MeineAbstrakteKlasse

    java -cp bin;lib/* classfinder.ClassFinder "" classfinder.MeineKlasse2

    java -cp bin;lib/* classfinder.ClassFinder "" org.junit.runner.Result

    Falls Sie eine andere als eine junit-...jar-Lib in das lib-Verzeichnis kopiert haben, müssen Sie die Klasse im letzten Kommando entsprechend anpassen.




Weitere Themen: andere TechDocs
© 2009 Torsten Horn, Aachen