NutzCN Logo
灌水 测试超长帖子
发布于 2981天前 作者 wendal 1530 次浏览 复制 上一个帖子 下一个帖子
标签:

package org.nutz.resource;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.servlet.ServletContext;

import org.nutz.Nutz;
import org.nutz.castor.Castors;
import org.nutz.lang.Encoding;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.util.ClassTools;
import org.nutz.lang.util.Disks;
import org.nutz.lang.util.FileVisitor;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.resource.impl.ErrorResourceLocation;
import org.nutz.resource.impl.FileResource;
import org.nutz.resource.impl.ResourceLocation;

/**
* 资源扫描的帮助函数集
*
* @author zozoh(zozohtnt@gmail.com)
*/
public class Scans {

/**
 * 在Web环境中使用Nutz的任何功能,都应该先调用这个方法,以初始化资源扫描器
 * <p/>
 * 调用一次就可以了
 */
@SuppressWarnings("unchecked")
public Scans init(ServletContext sc) {
    // 获取classes文件夹的路径
    String classesPath = sc.getRealPath("/WEB-INF/classes/");
    if (classesPath != null) {
        locations.add(ResourceLocation.file(new File(classesPath)));
    } else {
        if (log.isWarnEnabled())
            log.warn("/WEB-INF/classes/ NOT found?!");
    }

    // 获取lib文件夹中的全部jar
    Set<String> jars = sc.getResourcePaths("/WEB-INF/lib/");
    if (jars != null) { // 这个文件夹不一定存在,尤其是Maven的WebApp项目
        ExecutorService es = Executors.newFixedThreadPool(8);
        for (final String path : jars) {
            if (!path.toLowerCase().endsWith(".jar"))
                continue;
            es.submit(new Runnable() {
          public void run() {
              locations.add(ResourceLocation.jar(sc.getRealPath(path)));
          }
         });
        }
        es.shutdown();
        try {
         es.awaitTermination(1, TimeUnit.HOURS);
       } catch (InterruptedException e) {
       }
    }
    else {
        if (log.isWarnEnabled())
            log.warn("/WEB-INF/lib/ NOT found?!");
    }
    _print();
    return this;
}

public List<NutResource> loadResource(String regex, String... paths) {
    List<NutResource> list = new LinkedList<NutResource>();
    for (String path : paths) {
        list.addAll(scan(path, regex));
    }
    // 如果找不到?
    if (list.size() < 1 && paths.length > 0)
        throw Lang.makeThrow(    RuntimeException.class,
                                "folder or file like '%s' no found in %s",
                                regex,
                                Castors.me().castToString(paths));
    return new ArrayList<NutResource>((new LinkedHashSet<NutResource>(list)));
}

public void registerLocation(Class<?> klass) {
    if (klass == null)
        return;
    try {
        registerLocation(klass.getProtectionDomain().getCodeSource().getLocation());
    }
    catch (Throwable e) { // Android上会死
        String classFile = klass.getName().replace('.', '/') + ".class";
        URL url = ClassTools.getClassLoader().getResource(classFile);
        if (url != null) { // 基本上不可能为null
            String str = url.toString();
            try {
                str = URLDecoder.decode(str, Encoding.UTF8);
            }
            catch (UnsupportedEncodingException e1) {
                throw Lang.impossible();
            }
            str = str.substring(0, str.length() - classFile.length());
            try {
                registerLocation(new URL(str));
            }
            catch (Throwable e2) {
                if (log.isInfoEnabled())
                    log.info("Fail to registerLocation --> " + str, e);
            }
        }
    }
}

public void registerLocation(URL url) {
    if (url == null)
        return;
    locations.add(makeResourceLocation(url));
}

protected ResourceLocation makeResourceLocation(URL url) {
    try {
        String str = url.toString();
        if (str.endsWith(".jar")) {
            return ResourceLocation.jar(str);
        } else if (str.contains("jar!")) {
            if (str.startsWith("jar:file:")) {
               str = str.substring("jar:file:".length());
            }
            return ResourceLocation.jar(str.substring(0, str.lastIndexOf("jar!") + 3));
        } else if (str.startsWith("file:")) {
            return ResourceLocation.file(new File(url.getFile()));
        } else {
            if (log.isDebugEnabled())
                log.debug("Unkown URL " + url);
            //return ResourceLocation.file(new File(url.toURI()));
        }
    }
    catch (Throwable e) {
        if (log.isInfoEnabled())
            log.info("Fail to registerLocation --> " + url, e);
    }
    return ErrorResourceLocation.make(url);
}

public List<NutResource> scan(String src) {
    return scan(src, null);
}

/**
 * 在磁盘目录或者 CLASSPATH(包括 jar) 中搜索资源
 * <p/>
 * <b>核心方法</b>
 * 
 * @param src
 *            起始路径
 * @param regex
 *            资源名需要匹配的正则表达式
 * @return 资源列表
 */
public List<NutResource> scan(String src, String regex) {
    if (src.isEmpty())
        throw new RuntimeException("emtry src is NOT allow");
    if ("/".equals(src))
        throw new RuntimeException("root path is NOT allow");
    List<NutResource> list = new ArrayList<NutResource>();
    Pattern pattern = regex == null ? null : Pattern.compile(regex);
    // 先看看是不是文件系统上一个具体的文件
    if (src.startsWith("~/"))
        src = Disks.normalize(src);
    File srcFile = new File(src);
    if (srcFile.exists()) {
        if (srcFile.isDirectory()) {
            Disks.visitFile(srcFile,
                            new ResourceFileVisitor(list, src),
                            new ResourceFileFilter(pattern));
        } else {
            list.add(new FileResource(src, srcFile));
        }
    }
    for (ResourceLocation location : locations) {
        location.scan(src, pattern, list);
    }
    // 如果啥都没找到,那么,用增强扫描
    if (list.isEmpty()) {
        try {
            Enumeration<URL> enu = ClassTools.getClassLoader().getResources(src);
            if (enu != null && enu.hasMoreElements()) {
                while (enu.hasMoreElements()) {
                    try {
                        URL url = enu.nextElement();
                        ResourceLocation loc = makeResourceLocation(url);
                        if (url.toString().contains("jar!"))
                            loc.scan(src, pattern, list);
                        else
                            loc.scan("", pattern, list);
                    }
                    catch (Throwable e) {
                        if (log.isTraceEnabled())
                            log.trace("", e);
                    }
                }
            }
        }
        catch (Throwable e) {
            if (log.isDebugEnabled())
                log.debug("Fail to run deep scan!", e);
        }
    }
    ArrayList<NutResource> tmp = new ArrayList<NutResource>();
    for (NutResource r : list) {
        int index = tmp.indexOf(r);
        if (index > -1) {
            NutResource old = tmp.get(index);
            if (old.getSource() != null && r.getSource() != null && old.getSource().equals(r.getSource())) {
                continue;
            }
           log.infof("same resource path [%s](%s) will be override by [%s](%s)", 
                     tmp.get(index).getName(), tmp.get(index).getSource(),
                     r.getName(), r.getSource());
           tmp.set(index, r);
        } else
           tmp.add(r);
    }
    if (log.isDebugEnabled())
        log.debugf("Found %s resource by src( %s ) , regex( %s )", list.size(), src, regex);
    return list;
}

public List<Class<?>> scanPackage(Class<?> classZ) {
    return scanPackage(classZ.getPackage().getName(), FLT_CLASS);
}

public List<Class<?>> scanPackage(Class<?> classZ, String regex) {
    return scanPackage(classZ.getPackage().getName(), regex);
}

/**
 * 搜索并返回给定包下所有的类(递归)
 * 
 * @param pkg
 *            包名或者包路径
 */
public List<Class<?>> scanPackage(String pkg) {
    return scanPackage(pkg, FLT_CLASS);
}

/**
 * 搜索给定包下所有的类(递归),并返回所有符合正则式描述的类
 * 
 * @param pkg
 *            包名或者包路径
 * @param regex
 *            正则表达式,请注意你需要匹配的名称为 'xxxx.class' 而不仅仅是类名,从而保证选出的对象都是类文件
 */
public List<Class<?>> scanPackage(String pkg, String regex) {
    String packagePath = pkg.replace('.', '/').replace('\\', '/');
    if (!packagePath.endsWith("/"))
        packagePath += "/";
    return rs2class(pkg, scan(packagePath, regex));
}

public static boolean isInJar(File file) {
    return isInJar(file.getAbsolutePath());
}

public static boolean isInJar(String filePath) {
    return filePath.contains(".jar!");
}

public static NutResource makeJarNutResource(File file) {
    return makeJarNutResource(file.getAbsolutePath());
}

public static NutResource makeJarNutResource(String filePath) {
    JarEntryInfo jeInfo = new JarEntryInfo(filePath);
    try {
        ZipInputStream zis = makeZipInputStream(jeInfo.getJarPath());
        ZipEntry ens = null;
        while (null != (ens = zis.getNextEntry())) {
            if (ens.isDirectory())
                continue;
            if (jeInfo.getEntryName().equals(ens.getName())) {
                return makeJarNutResource(jeInfo.getJarPath(), ens.getName(), "");
            }
        }
    }
    catch (IOException e) {}
    return null;
}

public static NutResource makeJarNutResource(    final String jarPath,
                                                final String entryName,
                                                final String base) throws IOException {
    NutResource nutResource = new NutResource() {

        public InputStream getInputStream() throws IOException {
            ZipInputStream zis = makeZipInputStream(jarPath);
            ZipEntry ens = null;
            while (null != (ens = zis.getNextEntry())) {
                if (ens.getName().equals(entryName))
                    return zis;
            }
            throw Lang.impossible();
        }

        public int hashCode() {
            return (jarPath + ":" + entryName).hashCode();
        }
    };
    if (entryName.equals(base))
        nutResource.setName(entryName);
    else
        nutResource.setName(entryName.substring(base.length()));
    nutResource.setSource(jarPath + ":" + entryName);
    return nutResource;
}

public static ZipInputStream makeZipInputStream(String jarPath) throws MalformedURLException,
        IOException {
    ZipInputStream zis = null;
    try {
        zis = new ZipInputStream(new FileInputStream(jarPath));
    }
    catch (IOException e) {
        zis = new ZipInputStream(new URL(jarPath).openStream());
    }
    return zis;
}

public static final Scans me() {
    return me;
}

/**
 * 将一组 NutResource 转换成 class 对象
 * 
 * @param packagePath
 *            包前缀
 * @param list
 *            列表
 * @return 类对象列表
 */
private static List<Class<?>> rs2class(String pkg, List<NutResource> list) {
    Set<Class<?>> re = new HashSet<Class<?>>(list.size());
    if (!list.isEmpty()) {
        for (NutResource nr : list) {
            if (!nr.getName().endsWith(".class") || nr.getName().endsWith("package-info.class")) {
                continue;
            }
            // Class快速载入
            String className = pkg + "." + nr.getName().substring(0, nr.getName().length() - 6).replaceAll("[/\\\\]", ".");
            try {
                Class<?> klass = Lang.loadClass(className);
                re.add(klass);
          continue;
         }
         catch (Throwable e) {}
            // 失败了? 尝试终极方法,当然了,慢多了
            InputStream in = null;
            try {
                in = nr.getInputStream();
                className = ClassTools.getClassName(in);
                if (className == null) {
                    if (log.isInfoEnabled())
                        log.infof("Resource can't map to Class, Resource %s", nr);
                    continue;
                }
                Class<?> klass = Lang.loadClass(className);
                re.add(klass);
            }
            catch (Throwable e) {
                if (log.isInfoEnabled())
                    log.info("Resource can't map to Class, Resource " + nr.getName(), e);
            }
            finally {
                Streams.safeClose(in);
            }
        }
    }
    return new ArrayList<Class<?>>(re);
}

public static class ResourceFileFilter implements FileFilter {
    public boolean accept(File f) {
        if (f.isDirectory()) {
            String fnm = f.getName().toLowerCase();
            // 忽略 SVN 和 CVS 文件,还有Git文件
            if (".svn".equals(fnm) || ".cvs".equals(fnm) || ".git".equals(fnm))
                return false;
            return true;
        }
        if (f.isHidden())
            return false;
        return pattern == null || pattern.matcher(f.getName()).find();
    }

    private Pattern pattern;

    public ResourceFileFilter(Pattern pattern) {
        super();
        this.pattern = pattern;
    }
}

public static class ResourceFileVisitor implements FileVisitor {
    public void visit(File f) {
        list.add(new FileResource(base, f));
    }

    String base;
    List<NutResource> list;

    public ResourceFileVisitor(List<NutResource> list, String base) {
        super();
        this.list = list;
        this.base = base;
    }
}

private static final String FLT_CLASS = "^.+[.]class$";

private static final Log log = Logs.get();

private static final Scans me = new Scans();

private Set<ResourceLocation> locations = new HashSet<ResourceLocation>();

private Scans() {
    if (Lang.isAndroid) {
        if (log.isInfoEnabled())
            log.info("Running in Android , so nothing I can scan , just disable myself");
        return;
    }
    // 当前文件夹
    locations.add(ResourceLocation.file(new File(".")));
    // 推测一下nutz自身所在的位置
    registerLocation(Nutz.class);

    // 通过/META-INF/MANIFEST.MF等标记文件,获知所有jar文件的路径
    String[] referPaths = new String[]{    "META-INF/MANIFEST.MF",
                                        "log4j.properties",
                                        ".nutz.resource.mark"};
    ClassLoader cloader = ClassTools.getClassLoader();
    for (String referPath : referPaths) {
        try {
            Enumeration<URL> urls = cloader.getResources(referPath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String url_str = url.toString();
                if (url.toString().contains("jar!"))
                    url = new URL(url_str.substring(0, url_str.length()
                                                        - referPath.length()
                                                        - 2));
                else
                    url = new URL(url_str.substring(0, url_str.length() - referPath.length()));
                registerLocation(url);
            }
        }
        catch (IOException e) {}
    }

    // 把ClassPath也扫描一下
    try {
        String classpath = System.getProperties().getProperty("java.class.path");
        String[] paths = classpath.split(System.getProperties().getProperty("path.separator"));
        for (String pathZ : paths) {
            if (pathZ.endsWith(".jar"))
                locations.add(ResourceLocation.jar(pathZ));
            else
                locations.add(ResourceLocation.file(new File(pathZ)));
        }
    }
    catch (Throwable e) {
        // TODO: handle exception
    }

    _print();
}

public void _print() {
    if (log.isTraceEnabled())
        log.trace("Locations for Scans:\n" + locations);
    else if (log.isDebugEnabled())
        log.debugf("%-2d Locations for Scans", locations.size());
}

}

0 回复
添加回复
请先登陆
回到顶部