com.sinosoft.one.mvc.scanning.MvcScanner.java Source code

Java tutorial

Introduction

Here is the source code for com.sinosoft.one.mvc.scanning.MvcScanner.java

Source

/*
 * Copyright 2007-2009 the original author or authors.
 *
 * 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 com.sinosoft.one.mvc.scanning;

import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;

/**
 * @author zhiliang.wang  [qieqie.wang@gmail.com]
 */
public class MvcScanner {

    private static SoftReference<MvcScanner> softReference;

    public synchronized static MvcScanner getInstance() {
        if (softReference == null || softReference.get() == null) {
            MvcScanner mvcScanner = new MvcScanner();
            softReference = new SoftReference<MvcScanner>(mvcScanner);
        }
        return softReference.get();
    }

    // -------------------------------------------------------------

    protected Log logger = LogFactory.getLog(getClass());

    protected Date createTime = new Date();

    protected ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    private List<ResourceRef> classesFolderResources;

    private List<ResourceRef> jarResources;

    // -------------------------------------------------------------

    private MvcScanner() {
    }

    public Date getCreateTime() {
        return createTime;
    }

    // -------------------------------------------------------------
    public List<ResourceRef> getJarOrClassesFolderResources() throws IOException {
        return getJarOrClassesFolderResources(null);
    }

    public List<ResourceRef> getJarOrClassesFolderResources(String[] scope) throws IOException {
        if (logger.isInfoEnabled()) {
            logger.info("[findFiles] start to found classes folders " + "or mvcd jar files by scope:"
                    + Arrays.toString(scope));
        }
        List<ResourceRef> resources;
        if (scope == null) {
            resources = new LinkedList<ResourceRef>();
            if (logger.isDebugEnabled()) {
                logger.debug("[findFiles] call 'classesFolder'");
            }
            resources.addAll(getClassesFolderResources());
            //
            if (logger.isDebugEnabled()) {
                logger.debug("[findFiles] exits from 'classesFolder'");
                logger.debug("[findFiles] call 'jarFile'");
            }
            resources.addAll(getJarResources());
            if (logger.isDebugEnabled()) {
                logger.debug("[findFiles] exits from 'jarFile'");
            }
        } else if (scope.length == 0) {
            return new ArrayList<ResourceRef>();
        } else {
            resources = new LinkedList<ResourceRef>();
            for (String scopeEntry : scope) {
                String packagePath = scopeEntry.replace('.', '/');
                Resource[] packageResources = resourcePatternResolver.getResources("classpath*:" + packagePath);
                for (Resource pkgResource : packageResources) {
                    String uri = pkgResource.getURI().toString();
                    uri = StringUtils.removeEnd(uri, "/");
                    packagePath = StringUtils.removeEnd(packagePath, "/");
                    uri = StringUtils.removeEnd(uri, packagePath);
                    int beginIndex = uri.lastIndexOf("file:");
                    if (beginIndex == -1) {
                        beginIndex = 0;
                    } else {
                        beginIndex += "file:".length();
                    }
                    int endIndex = uri.lastIndexOf('!');
                    if (endIndex == -1) {
                        endIndex = uri.length();
                    }
                    String path = uri.substring(beginIndex, endIndex);
                    Resource folder = new FileSystemResource(path);
                    ResourceRef ref = ResourceRef.toResourceRef(folder);
                    if (!resources.contains(ref)) {
                        resources.add(ref);
                        if (logger.isDebugEnabled()) {
                            logger.debug(
                                    "[findFiles] found classes folders " + "or mvcd jar files by scope:" + ref);
                        }
                    }
                }
            }
        }
        //
        if (logger.isInfoEnabled()) {
            logger.info("[findFiles] found " + resources.size() + " classes folders " + "or mvcd jar files : "
                    + resources);
        }

        return resources;
    }

    /**
     * ????(WEB-INF/classestarget/classes?)
     * 
     * @return
     * @throws IOException
     * @throws URISyntaxException
     */
    public List<ResourceRef> getClassesFolderResources() throws IOException {
        if (classesFolderResources == null) {
            if (logger.isInfoEnabled()) {
                logger.info("[classesFolder] start to found available classes folders ...");
            }
            List<ResourceRef> classesFolderResources = new ArrayList<ResourceRef>();
            Enumeration<URL> founds = resourcePatternResolver.getClassLoader().getResources("");
            while (founds.hasMoreElements()) {
                URL urlObject = founds.nextElement();
                if (!"file".equals(urlObject.getProtocol())) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("[classesFolder] Ignored classes folder because " + "not a file protocol url: "
                                + urlObject);
                    }
                    continue;
                }
                String path = urlObject.getPath();
                Assert.isTrue(path.endsWith("/"));
                if (!path.endsWith("/classes/") && !path.endsWith("/test-classes/") && !path.endsWith("/bin/")) {
                    if (logger.isInfoEnabled()) {
                        logger.info("[classesFolder] Ignored classes folder because "
                                + "not ends with '/classes/' or '/bin/': " + urlObject);
                    }
                    continue;
                }
                File file;
                try {
                    file = new File(urlObject.toURI());
                } catch (URISyntaxException e) {
                    throw new IOException(e.getLocalizedMessage());
                }
                if (file.isFile()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("[classesFolder] Ignored because not a directory: " + urlObject);
                    }
                    continue;
                }
                Resource resource = new FileSystemResource(file);
                ResourceRef resourceRef = ResourceRef.toResourceRef(resource);
                if (classesFolderResources.contains(resourceRef)) {
                    // ???
                    if (logger.isDebugEnabled()) {
                        logger.debug("[classesFolder] remove replicated classes folder: " + resourceRef);
                    }
                } else {
                    classesFolderResources.add(resourceRef);
                    if (logger.isDebugEnabled()) {
                        logger.debug("[classesFolder] add classes folder: " + resourceRef);
                    }
                }
            }
            // ?????
            Collections.sort(classesFolderResources);
            List<ResourceRef> toRemove = new LinkedList<ResourceRef>();
            for (int i = 0; i < classesFolderResources.size(); i++) {
                ResourceRef ref = classesFolderResources.get(i);
                String refURI = ref.getResource().getURI().toString();
                for (int j = i + 1; j < classesFolderResources.size(); j++) {
                    ResourceRef refj = classesFolderResources.get(j);
                    String refjURI = refj.getResource().getURI().toString();
                    if (refURI.startsWith(refjURI)) {
                        toRemove.add(refj);
                        if (logger.isInfoEnabled()) {
                            logger.info("[classesFolder] remove wrapper classes folder: " //
                                    + refj);
                        }
                    } else if (refjURI.startsWith(refURI) && refURI.length() != refjURI.length()) {
                        toRemove.add(ref);
                        if (logger.isInfoEnabled()) {
                            logger.info("[classesFolder] remove wrapper classes folder: " //
                                    + ref);
                        }
                    }
                }
            }
            classesFolderResources.removeAll(toRemove);
            //
            this.classesFolderResources = new ArrayList<ResourceRef>(classesFolderResources);
            if (logger.isInfoEnabled()) {
                logger.info("[classesFolder] found " + classesFolderResources.size() + " classes folders: "
                        + classesFolderResources);
            }
        } else {
            if (logger.isInfoEnabled()) {
                logger.info("[classesFolder] found cached " + classesFolderResources.size() + " classes folders: "
                        + classesFolderResources);
            }
        }
        return Collections.unmodifiableList(classesFolderResources);
    }

    /**
     * ???jar?
     * @return
     * @throws IOException
     */
    public List<ResourceRef> getJarResources() throws IOException {
        if (jarResources == null) {
            if (logger.isInfoEnabled()) {
                logger.info("[jarFile] start to found available jar files for mvc to scanning...");
            }
            List<ResourceRef> jarResources = new LinkedList<ResourceRef>();
            Resource[] metaInfResources = resourcePatternResolver.getResources("classpath*:/META-INF/");
            for (Resource metaInfResource : metaInfResources) {
                URL urlObject = metaInfResource.getURL();
                if (ResourceUtils.isJarURL(urlObject)) {
                    try {
                        String path = URLDecoder.decode(urlObject.getPath(), "UTF-8"); // fix 20%
                        if (path.startsWith("file:")) {
                            path = path.substring("file:".length(), path.lastIndexOf('!'));
                        } else {
                            path = path.substring(0, path.lastIndexOf('!'));
                        }
                        Resource resource = new FileSystemResource(path);
                        if (jarResources.contains(resource)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("[jarFile] skip replicated jar resource: " + path);//  linux ???,fix it!
                            }
                        } else {
                            ResourceRef ref = ResourceRef.toResourceRef(resource);
                            if (ref.getModifiers() != null) {
                                jarResources.add(ref);
                                if (logger.isInfoEnabled()) {
                                    logger.info("[jarFile] add jar resource: " + ref);
                                }
                            } else {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("[jarFile] not mvc jar resource: " + path);
                                }
                            }
                        }
                    } catch (Exception e) {
                        logger.error(urlObject, e);
                    }
                } else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("[jarFile] not mvc type(not a jar) " + urlObject);
                    }
                }
            }
            this.jarResources = jarResources;
            if (logger.isInfoEnabled()) {
                logger.info("[jarFile] found " + jarResources.size() + " jar files: " + jarResources);
            }
        } else {
            if (logger.isInfoEnabled()) {
                logger.info("[jarFile] found cached " + jarResources.size() + " jar files: " + jarResources);
            }
        }
        return Collections.unmodifiableList(jarResources);
    }

}