Package org.jboss.errai.bus.server.util

Source Code of org.jboss.errai.bus.server.util.ConfigUtil

/*
* Copyright 2009 JBoss, a divison Red Hat, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.errai.bus.server.util;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.user.rebind.SourceWriter;
import org.jboss.errai.bus.server.ErraiBootstrapFailure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
* Contains methods used for configuring and bootstrapping Errai.
*/
public class ConfigUtil {
    public static final String ERRAI_CONFIG_STUB_NAME = "ErraiApp.properties";
    public static final Logger log = LoggerFactory.getLogger(ConfigUtil.class);

    /**
     * Gets a list of all the configuration targets in the form of <tt>File</tt>s
     *
     * @return a <tt>File</tt> list of all the configuration targets
     */
    public static List<File> findAllConfigTargets() {
        try {
            Enumeration<URL> t = ConfigUtil.class.getClassLoader().getResources(ERRAI_CONFIG_STUB_NAME);
            List<File> targets = new LinkedList<File>();
            while (t.hasMoreElements()) {
                String fileName = URLDecoder.decode(t.nextElement().getFile(), "UTF-8");

                // this is referencing a file inside a compressed archive.
                int trimIdx = fileName.lastIndexOf("!");
                if (trimIdx != -1) {
                    // get the path to the archive
                    fileName = fileName.substring(0, trimIdx);
                }

                // if it starts with a URI scheme prefix, let's strip it away.
                if (fileName.startsWith("file:/")) {
                    fileName = fileName.substring(5);
                }

                // we don't want to bother with source JARs.
                if (fileName.endsWith("-sources.jar")) {
                    continue;
                }

                // obtain a File object
                File file = new File(fileName);

                // If this is a direct filesystem path, we get the parent file (directory)
                targets.add(trimIdx == -1 ? file.getParentFile() : file);
            }

            log.info("configuration scan targets");
            for (File tg : targets) {
                log.info(" -> " + tg.getPath());
            }

            return targets;
        }
        catch (Exception e) {
            throw new ErraiBootstrapFailure("could not locate config target paths", e);
        }
    }

    private static Map<String, File> scanAreas = new HashMap<String, File>();
    private static Map<String, List<Class>> scanCache = new HashMap<String, List<Class>>();
    private static Set<String> activeCacheContexts = new HashSet<String>();

    private static String tmpUUID = "erraiBootstrap_" + UUID.randomUUID().toString().replaceAll("\\-", "_");

    private static void recordCache(String context, Class cls) {
        if (scanCache == null) return;
       
        List<Class> cache = scanCache.get(context);

        if (cache == null) {
            log.info("caching context '" + context + "'");
            scanCache.put(context, cache = new LinkedList<Class>());
        }

        cache.add(cls);
    }

    /**
     * Cleans up the startup temporary files, including those stored under the system's temp directory
     */
    public static void cleanupStartupTempFiles() {
        if (scanAreas == null) return;
       
        log.info("Cleaning up ...");
        for (File f : scanAreas.values()) {
            f.delete();
        }
        new File(System.getProperty("java.io.tmpdir") + "/" + tmpUUID).delete();
        scanAreas = null;
        scanCache = null;
    }

    /**
     * Visits all targets that can be found under <tt>root</tt>, using the <tt>ConfigVisitor</tt> specified
     *
     * @param root - the root file to start visiting from
     * @param visitor - the visitor delegate to use
     */
    public static void visitAll(File root, final ConfigVisitor visitor) {
        _findLoadableModules(root, root, new HashSet<String>(), new VisitDelegate() {
            public void visit(Class clazz) {
                visitor.visit(clazz);
            }
        });

        if (activeCacheContexts != null) activeCacheContexts.add(root.getPath());
    }

    /**
     * Visits all the targets listed in the file, using the <tt>ConfigVisitor</tt> specified
     *
     * @param targets - the file targets to visit
     * @param visitor - the visitor delegate to use
     */
    public static void visitAllTargets(List<File> targets, ConfigVisitor visitor) {
        for (File file : targets) {
            visitAll(file, visitor);
        }
    }

    /**
     * Visits all targets that can be found under <tt>root</tt>
     *
     * @param root - the root file to start visiting from
     * @param context - provides metadata to deferred binding generators
     * @param logger - log messages in deferred binding generators
     * @param writer - supports the source file regeneration
     * @param visitor - the visitor delegate to use
     */
    public static void visitAll(File root, final GeneratorContext context, final TreeLogger logger,
                                final SourceWriter writer, final RebindVisitor visitor) {
        _findLoadableModules(root, root, new HashSet<String>(), new VisitDelegate() {
            public void visit(Class clazz) {
                visitor.visit(clazz, context, logger, writer);
            }
        });

        if (activeCacheContexts != null) activeCacheContexts.add(root.getPath());
    }

    /**
     * Visits all the file targets specified in the list using the <tt>RebindVisitor</tt>
     *
     * @param targets - the file targets to visit
     * @param context - provides metadata to deferred binding generators
     * @param logger - log messages in deferred binding generators
     * @param writer - supports the source file regeneration
     * @param visitor - the visitor delegate to use
     */
    public static void visitAllTargets(List<File> targets, final GeneratorContext context,
                                       final TreeLogger logger, final SourceWriter writer, RebindVisitor visitor) {
        for (File file : targets) {
            visitAll(file, context, logger, writer, visitor);
        }
    }

    private static void _findLoadableModules(File root, File start, Set<String> loadedTargets, VisitDelegate visitor) {
        if (start.isDirectory()) {
            loadFromDirectory(root, start, loadedTargets, visitor);
        } else if (start.isFile()) {
            loadFromZippedResource(root, start, loadedTargets, visitor, null);
        } else {
            /**
             * This path is not directly resolvable to a directory or file target, which may mean that it's a
             * virtual path.  So in order to over-come this, we'll perform a brute-force reparsing of the URI
             * until we find something to work with.
             */
            findValidPath(root, start, loadedTargets, visitor);
        }
    }

    private static void findValidPath(File root, File start, Set<String> loadedTargets, VisitDelegate visitor) {
        String originalPath = start.getPath();
        int pivotPoint;

        String rootPath;
        do {
            start = new File((rootPath = start.getPath())
                    .substring(0, (pivotPoint = rootPath.lastIndexOf("/")) < 0 ? 0 : pivotPoint));

        } while (!start.isFile() && pivotPoint > 0);

        if (start.isFile()) {
            loadFromZippedResource(root, start, loadedTargets, visitor, originalPath.substring(pivotPoint + 1));
        }
    }

    private static String CLASS_RESOURCES_ROOT = "WEB-INF.classes.";

    private static void loadFromZippedResource(File root, File start, Set<String> loadedTargets, VisitDelegate visitor,
                                               String scanFilter) {

        final String pathToJar = start.getPath();
        boolean startsWithFile = pathToJar.startsWith("file:/");

        InputStream inStream = null;
        try {
            if (!pathToJar.matches(".+\\.(zip|jar|war)$")) return;

            int startIdx = startsWithFile ? 5 : 0;
            int endIdx = pathToJar.lastIndexOf(".jar");
            if (endIdx == -1) endIdx = pathToJar.lastIndexOf(".war");
            if (endIdx == -1) endIdx = pathToJar.lastIndexOf(".zip");

            if (endIdx == -1) {
                endIdx = pathToJar.length() - 1;
            } else {
                endIdx += 4;
            }

            String jarName = pathToJar.substring(startIdx, endIdx);

            inStream = findResource(ConfigUtil.class.getClassLoader(), jarName.replaceAll("/", "\\."));

            if (inStream == null) {
                /**
                 * Try to load this directly as a file.
                 */
                inStream = new FileInputStream(jarName);
            }
            loadZipFromStream(jarName, inStream, loadedTargets, visitor, scanFilter);

        }
        catch (Exception e) {
            log.warn("did not process '" + pathToJar + "' (probably non-fatal)", e);
        }
        finally {
            try {
                if (inStream != null) inStream.close();
            }
            catch (Exception e) {
                log.error("failed to close stream", e);
            }
        }
    }

    private static void loadZipFromStream(String zipName, InputStream inStream, Set<String> loadedTargets,
                                          VisitDelegate visitor, String scanFilter) throws IOException {
        ZipInputStream zipFile = new ZipInputStream(inStream);
        ZipEntry zipEntry;

        String ctx = zipName + (scanFilter == null ? ":*" : ":" + scanFilter);

        if (activeCacheContexts != null && scanCache != null &&
                activeCacheContexts.contains(ctx) && scanCache.containsKey(ctx)) {
            List<Class> cache = scanCache.get(ctx);
            for (Class loadClass : cache) {
                visitor.visit(loadClass);
            }
        } else {
            while ((zipEntry = zipFile.getNextEntry()) != null) {
                if (scanFilter != null && !zipEntry.getName().startsWith(scanFilter)) continue;

                if (zipEntry.getName().endsWith(".class")) {

                    String classEntry;
                    String className = null;
                    boolean cached = false;
                    try {
                        classEntry = zipEntry.getName().replaceAll("/", "\\.");
                        int beginIdx = classEntry.indexOf(CLASS_RESOURCES_ROOT);
                        if (beginIdx == -1) {
                            beginIdx = 0;
                        } else {
                            beginIdx += CLASS_RESOURCES_ROOT.length();
                        }

                        className = classEntry.substring(beginIdx, classEntry.lastIndexOf(".class"));
                        Class<?> loadClass = Class.forName(className);

                        recordCache(ctx, loadClass);

                        cached = true;

                        visitor.visit(loadClass);
                    }
                    catch (Throwable e) {
                        if (!cached) {
                            log.trace("Failed to load: " + className
                                    + "(" + e.getMessage() + ") -- Probably non-fatal.");
                        }
                    }

                } else if (zipEntry.getName().matches(".+\\.(zip|jar|war)$")) {
                    /**
                     * Let's decompress this to a temp dir so we can look at it:
                     */

                    InputStream tmpZipStream = null;
                    try {

                        if (scanAreas.containsKey(zipEntry.getName())) {
                            File tmpFile = scanAreas.get(zipEntry.getName());
                            tmpZipStream = new FileInputStream(tmpFile);
                            loadZipFromStream(tmpFile.getName(), tmpZipStream, loadedTargets, visitor, null);
                        } else {
                            File tmpUnZip = expandZipEntry(zipFile, zipEntry);

                            scanAreas.put(zipEntry.getName(), tmpUnZip);

                            tmpZipStream = new FileInputStream(tmpUnZip);

                            loadZipFromStream(tmpUnZip.getName(), tmpZipStream, loadedTargets, visitor, null);
                        }
                    }
                    finally {
                        if (tmpZipStream != null) {
                            tmpZipStream.close();
                        }
                    }
                }
            }

            activeCacheContexts.add(zipName);
        }
    }

    private static File expandZipEntry(ZipInputStream stream, ZipEntry entry) {

        String tmpDir = System.getProperty("java.io.tmpdir") + "/" + tmpUUID;
        int idx = entry.getName().lastIndexOf('/');
        String tmpFileName = tmpDir + "/" + entry.getName().substring(idx == -1 ? 0 : idx);
        try {
            File tmpDirFile = new File(tmpDir);
            tmpDirFile.mkdirs();

            File newFile = new File(tmpFileName);

            FileOutputStream outStream = new FileOutputStream(newFile);
            byte[] buf = new byte[1024];
            int read;
            while ((read = stream.read(buf)) != -1) {
                outStream.write(buf, 0, read);
            }

            outStream.flush();
            outStream.close();

            newFile.getParentFile().deleteOnExit();

            return newFile;
        }
        catch (Exception e) {
            log.error("error reading from stream", e);
            return null;
        }
    }


    private static void loadFromDirectory(File root, File start, Set<String> loadedTargets, VisitDelegate visitor) {
        if (scanCache != null && activeCacheContexts != null && activeCacheContexts.contains(root.getPath())
                && scanCache.containsKey(root.getPath())) {
            for (Class loadClass : scanCache.get(root.getPath())) {
                visitor.visit(loadClass);
            }
        } else {
            for (File file : start.listFiles()) {
                if (file.isDirectory()) _findLoadableModules(root, file, loadedTargets, visitor);
                if (file.getName().endsWith(".class")) {
                    try {
                        String FQCN = getCandidateFQCN(root.getAbsolutePath(), file.getAbsolutePath());

                        if (loadedTargets.contains(FQCN)) {
                            return;
                        } else {
                            loadedTargets.add(FQCN);
                        }

                        Class<?> loadClass = Class.forName(FQCN);

                        recordCache(root.getPath(), loadClass);

                        visitor.visit(loadClass);
                    }
                    catch (NoClassDefFoundError e) {
                        // do nothing.
                    }
                    catch (ExceptionInInitializerError e) {
                        // do nothing.
                    }
                    catch (UnsupportedOperationException e) {
                        // do nothing.
                    }
                    catch (ClassNotFoundException e) {
                        // do nothing.
                    }
                    catch (UnsatisfiedLinkError e) {
                        // do nothing.
                    }
                    catch (Throwable t) {
                        // do nothing.

//                        t.printStackTrace();
//                        throw new ErraiBootstrapFailure("unknown error while visiting: " + file.getName() + ": " + t.getClass().getName() + ":" + t.getMessage(), t);
                    }
                }
            }
        }
    }

    private static InputStream findResource(ClassLoader loader, String resourceName) {
        ClassLoader cl = loader;
        InputStream is;

        while ((is = cl.getResourceAsStream(resourceName)) == null && (cl = cl.getParent()) != null) ;

        return is;
    }

    private static String getCandidateFQCN(String rootFile, String fileName) {
        return fileName.replaceAll("(/|\\\\)", ".")
                .substring(rootFile.length() + 1, fileName.lastIndexOf('.'));
    }

    /**
     * Returns true if the specified class, <tt>clazz</tt>, has annotations from the class <tt>annotation</tt>. Also,
     * checks that <tt>clazz</tt> is represented by <tt>ofType</tt>
     *
     * @param clazz - the class to check for the annotations
     * @param annotation - the annotations to look for
     * @param ofType - the class type we want to be sure to check, as <tt>clazz</tt> could be a visitor delegate.
     * @return true if the <tt>clazz</tt> has those <tt>annotation</tt>s
     */
    public static boolean isAnnotated(Class clazz, Class<? extends Annotation> annotation, Class ofType) {
        return ofType.isAssignableFrom(clazz) && clazz.isAnnotationPresent(annotation);
    }
}
TOP

Related Classes of org.jboss.errai.bus.server.util.ConfigUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.