org.opt4j.config.ModuleAutoFinder.java Source code

Java tutorial

Introduction

Here is the source code for org.opt4j.config.ModuleAutoFinder.java

Source

/**
 * Opt4J is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * Opt4J is distributed in the hope that it will be useful, but WITHOUT
 * ANY 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 along with Opt4J. If not, see http://www.gnu.org/licenses/. 
 */

package org.opt4j.config;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.commons.collections15.Transformer;
import org.opt4j.config.annotations.Ignore;
import org.opt4j.start.Opt4JModule;

import com.google.inject.Inject;
import com.google.inject.Module;

/**
 * The {@link ModuleAutoFinder} searches the classpath for all
 * {@link PropertyModule}s that are not annotated with {@link Ignore}.
 * 
 * @author lukasiewycz
 * 
 */
public class ModuleAutoFinder implements ModuleList {

    protected final Transformer<Class<? extends Module>, Boolean> accept;

    protected final Transformer<Class<? extends Module>, Boolean> ignore;

    protected final Set<ModuleAutoFinderListener> listeners = new CopyOnWriteArraySet<ModuleAutoFinderListener>();

    protected final Collection<String> directories;

    protected ClassLoader classLoader;

    /**
     * The {@link AllTrue} is a transformer that always returns {@code true}.
     * 
     * @author lukasiewycz
     * 
     */
    private static class AllTrue implements Transformer<Class<? extends Module>, Boolean> {
        @Override
        public Boolean transform(Class<? extends Module> arg) {
            return true;
        }
    }

    /**
     * The {@link AllFalse} is a transformer that always returns {@code false}.
     * 
     * @author lukasiewycz
     * 
     */
    private static class AllFalse implements Transformer<Class<? extends Module>, Boolean> {
        @Override
        public Boolean transform(Class<? extends Module> arg) {
            return false;
        }
    }

    /**
     * Constructs a {@link ModuleAutoFinder}.
     */
    @Inject
    public ModuleAutoFinder() {
        this(null, null, null);
    }

    /**
     * Constructs a {@link ModuleAutoFinder}.
     * 
     * @param accept
     *            the accept transformer
     * @param ignore
     *            the ignore transformer
     * @param directories
     *            additional directories for the classpath
     */
    public ModuleAutoFinder(Transformer<Class<? extends Module>, Boolean> accept,
            Transformer<Class<? extends Module>, Boolean> ignore, Collection<String> directories) {
        super();
        this.accept = (accept != null) ? accept : new AllTrue();
        this.ignore = (ignore != null) ? ignore : new AllFalse();
        this.directories = (directories != null) ? directories : new ArrayList<String>();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.opt4j.conf.ModuleFinder#getModules()
     */
    @Override
    public Collection<Class<? extends Module>> getModules() {
        return getAll();
    }

    /**
     * Returns all not abstract classes that implement {@link PropertyModule}.
     * 
     * @return all property modules
     */
    protected Collection<Class<? extends Module>> getAll() {

        Starter starter = new Starter();
        Collection<File> files = starter.addPlugins();

        classLoader = ClassLoader.getSystemClassLoader();

        String paths = System.getProperty("java.class.path");
        StringTokenizer st = new StringTokenizer(paths, ";\n:");

        while (st.hasMoreTokens()) {
            String path = st.nextToken();
            File f = new File(path);

            if (f.exists()) {
                try {
                    f = f.getCanonicalFile();
                    files.add(f);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        List<Class<?>> classes = new ArrayList<Class<?>>();

        for (File file : files) {

            if (isJar(file)) {

                try {
                    classes.addAll(getAllClasses(new ZipFile(file)));
                } catch (ZipException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (UnsupportedClassVersionError e) {
                    System.err.println(file + " not supported: bad version number");
                }
            } else {
                classes.addAll(getAllClasses(file));
            }
        }

        List<Class<? extends Module>> modules = new ArrayList<Class<? extends Module>>();

        for (Class<?> clazz : classes) {
            if (Opt4JModule.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
                Class<? extends Module> module = clazz.asSubclass(Module.class);
                Ignore i = module.getAnnotation(Ignore.class);

                if (i == null && !module.isAnonymousClass() && accept.transform(module)
                        && !ignore.transform(module)) {
                    modules.add(module);
                    invokeOut("Add module: " + module.toString());
                }
            }
        }

        return modules;

    }

    /**
     * Returns {@code true} if the file is a Jar archive.
     * 
     * @param file
     *            the tested file
     * @return {@code true} if the file is a Jar archive
     */
    protected boolean isJar(File file) {
        if (file.isDirectory()) {
            return false;
        }

        try {
            ZipFile zf = new ZipFile(file);
            zf.close();
            return true;
        } catch (ZipException e) {

        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Retrieves all Classes from one {@code directory}.
     * 
     * @param directory
     *            the directory
     * @return all classes in a list
     */
    protected List<Class<?>> getAllClasses(File directory) {
        return getAllClasses(directory, directory);
    }

    /**
     * Recursive methods searching for classes in a root directory.
     * 
     * @param root
     *            the root directory
     * @param file
     *            the current file
     * @return the list of all found classes
     */
    protected List<Class<?>> getAllClasses(File root, File file) {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                classes.addAll(getAllClasses(root, f));
            }
        } else {
            int rootLength = root.getAbsolutePath().length();
            String s = file.getAbsolutePath().substring(rootLength + 1);
            s = s.replace("\\", ".");
            s = s.replace("/", ".");
            if (s.endsWith(".class")) {
                s = s.substring(0, s.length() - 6);
                try {
                    Class<?> clazz = classLoader.loadClass(s);
                    classes.add(clazz);
                    invokeOut("Check: " + clazz.getName());
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (UnsupportedClassVersionError e) {
                    System.err.println(s + " not supported: bad version number");
                    invokeErr(s + " not supported");
                }
            }
        }

        return classes;

    }

    /**
     * Retrieves all Classes from a {@link ZipFile} (Jar archive).
     * 
     * @param zipFile
     *            the Jar archive
     * @return the list of all classes
     */
    protected List<Class<?>> getAllClasses(ZipFile zipFile) {
        invokeOut(zipFile.toString());
        List<Class<?>> classes = new ArrayList<Class<?>>();

        List<? extends ZipEntry> entries = Collections.list(zipFile.entries());
        for (int i = 0; i < entries.size(); i++) {

            ZipEntry entry = entries.get(i);

            String s = entry.getName();

            if (s.endsWith(".class")) {

                s = s.replace("/", ".");
                s = s.substring(0, s.length() - 6);

                try {
                    Class<?> clazz = classLoader.loadClass(s);
                    classes.add(clazz);
                    invokeOut("Check: " + clazz.getName());
                } catch (ClassNotFoundException e) {
                } catch (NoClassDefFoundError e) {
                } catch (UnsupportedClassVersionError e) {
                    System.err.println(s + " not supported: bad version number");
                    invokeErr(s + " not supported");
                }

            }
        }

        return classes;
    }

    /**
     * Invoke an out message for the {@link ModuleAutoFinderListener}.
     * 
     * @param message
     *            the message
     */
    protected void invokeOut(String message) {
        for (ModuleAutoFinderListener listener : listeners) {
            listener.out(message);
        }
    }

    /**
     * Invoke an err message for the {@link ModuleAutoFinderListener}.
     * 
     * @param message
     *            the message
     */
    protected void invokeErr(String message) {
        for (ModuleAutoFinderListener listener : listeners) {
            listener.out(message);
        }
    }

    /**
     * Add a {@link ModuleAutoFinderListener}.
     * 
     * @param listener
     *            the listener to be added
     */
    public void addListener(ModuleAutoFinderListener listener) {
        listeners.add(listener);
    }

    /**
     * Remove a {@link ModuleAutoFinderListener}.
     * 
     * @see #addListener
     * @param listener
     *            the listener to be removed
     */
    public void removeListener(ModuleAutoFinderListener listener) {
        listeners.remove(listener);
    }

}