groovyclassloader.java

来自「Groovy动态语言 运行在JVM中的动态语言 可以方便的处理业务逻辑变化大的业」· Java 代码 · 共 854 行 · 第 1/3 页

JAVA
854
字号
     * 
     * @see #isSourceNewer(URL, Class)
     * @param cls the class to be tested. If null the method should return true
     * @return true if the class should be compiled again
     */
    protected boolean isRecompilable(Class cls) {
        if (cls==null) return true;
        if (recompile==null && !config.getRecompileGroovySource()) return false;
        if (recompile!=null && !recompile.booleanValue()) return false;
        if (!GroovyObject.class.isAssignableFrom(cls)) return false;
        long timestamp = getTimeStamp(cls); 
        if (timestamp == Long.MAX_VALUE) return false;
        
        return true;
    }
    
    /**
     * sets if the recompilation should be enable. There are 3 possible
     * values for this. Any value different than null overrides the
     * value from the compiler configuration. true means to recompile if needed
     * false means to never recompile.  
     * @param mode the recompilation mode
     * @see CompilerConfiguration
     */
    public void setShouldRecompile(Boolean mode){
        recompile = mode;
    }
    
    
    /**
     * gets the currently set recompilation mode. null means, the 
     * compiler configuration is used. False means no recompilation and 
     * true means that recompilation will be done if needed. 
     * @return the recompilation mode
     */
    public Boolean isShouldRecompile(){
        return recompile;
    }

    /**
     * loads a class from a file or a parent classloader.
     *
     * @param name                      of the class to be loaded
     * @param lookupScriptFiles         if false no lookup at files is done at all
     * @param preferClassOverScript     if true the file lookup is only done if there is no class
     * @param resolve                   @see ClassLoader#loadClass(java.lang.String, boolean)
     * @return                          the class found or the class created from a file lookup
     * @throws ClassNotFoundException
     */
    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve)
        throws ClassNotFoundException, CompilationFailedException
    {
        // look into cache
        Class cls=getClassCacheEntry(name);
        
        // enable recompilation?
        boolean recompile = isRecompilable(cls);
        if (!recompile) return cls;

        // check security manager
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            String className = name.replace('/', '.');
            int i = className.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(className.substring(0, i));
            }
        }

        // try parent loader
        ClassNotFoundException last = null;
        try {
            Class parentClassLoaderClass = super.loadClass(name, resolve);
            // always return if the parent loader was successfull 
            if (cls!=parentClassLoaderClass) return parentClassLoaderClass;
        } catch (ClassNotFoundException cnfe) {
            last = cnfe;
        } catch (NoClassDefFoundError ncdfe) {
            if (ncdfe.getMessage().indexOf("wrong name")>0) {
                last = new ClassNotFoundException(name);
            } else {
                throw ncdfe;
            }
        }

        if (cls!=null) {
            // prefer class if no recompilation
            preferClassOverScript |= !recompile;
            if (preferClassOverScript) return cls;
        }

        // at this point the loading from a parent loader failed
        // and we want to recompile if needed.
        if (lookupScriptFiles) {
            // synchronize on cache, as we want only one compilation
            // at the same time
            synchronized (classCache) {
                // try groovy file
                try {
                    // check if recompilation already happend.
                    if (getClassCacheEntry(name)!=cls) return getClassCacheEntry(name);
                    URL source = resourceLoader.loadGroovySource(name);
                    cls = recompile(source,name,cls);
                } catch (IOException ioe) {
                    last = new ClassNotFoundException("IOException while openening groovy source: " + name, ioe);
                } finally {
                    if (cls==null) {
                        removeClassCacheEntry(name);
                    } else {
                        setClassCacheEntry(cls);
                    }
                }
            }
        }

        if (cls==null) {
            // no class found, there has to be an exception before then
            if (last==null) throw new AssertionError(true);
            throw last;
        }
        return cls;
    }

    /**
     * (Re)Comipiles the given source. 
     * This method starts the compilation of a given source, if
     * the source has changed since the class was created. For
     * this isSourceNewer is called.
     * 
     * @see #isSourceNewer(URL, Class)
     * @param source the source pointer for the compilation
     * @param className the name of the class to be generated
     * @param oldClass a possible former class
     * @return the old class if the source wasn't new enough, the new class else
     * @throws CompilationFailedException if the compilation failed
     * @throws IOException if the source is not readable
     * 
     */
    protected Class recompile(URL source, String className, Class oldClass) throws CompilationFailedException, IOException {
        if (source != null) {
            // found a source, compile it if newer
            if ((oldClass!=null && isSourceNewer(source, oldClass)) || (oldClass==null)) {
                sourceCache.remove(className);
                return parseClass(source.openStream(),className);
            }
        }
        return oldClass;
    }

    /**
     * Implemented here to check package access prior to returning an
     * already loaded class.
     * @throws CompilationFailedException if the compilation failed
     * @throws ClassNotFoundException if the class was not found
     * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
     */
    protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
        return loadClass(name,true,false,resolve);
    }

    /**
     * gets the time stamp of a given class. For groovy
     * generated classes this usually means to return the value
     * of the static field __timeStamp. If the parameter doesn't
     * have such a field, then Long.MAX_VALUE is returned
     * 
     * @param cls the class 
     * @return the time stamp
     */
    protected long getTimeStamp(Class cls) {
        Long o;
        try {
            Field field = cls.getField(Verifier.__TIMESTAMP);
            o = (Long) field.get(null);
        } catch (Exception e) {
            return Long.MAX_VALUE;
        }
        return o.longValue();
    }

    private URL getSourceFile(String name) {
        String filename = name.replace('.', '/') + config.getDefaultScriptExtension();
        URL ret = getResource(filename);
        if (ret!=null && ret.getProtocol().equals("file")) {
            String fileWithoutPackage = filename;
            if (fileWithoutPackage.indexOf('/')!=-1){
                int index = fileWithoutPackage.lastIndexOf('/');
                fileWithoutPackage = fileWithoutPackage.substring(index+1);
            }
            File path = new File(ret.getFile()).getParentFile();
            if (path.exists() && path.isDirectory()) {
                File file = new File(path, fileWithoutPackage);
                if (file.exists()) {
                    // file.exists() might be case insensitive. Let's do
                    // case sensitive match for the filename
                    File parent = file.getParentFile();
                    String[] files = parent.list();
                    for (int j = 0; j < files.length; j++) {
                        if (files[j].equals(fileWithoutPackage)) return ret;
                    }
                }
            }
            //file does not exist!
            return null;
        }
        return ret;
    }

    /**
     * Decides if the given source is newer than a class.
     * 
     * @see #getTimeStamp(Class)
     * @param source the source we may want to compile
     * @param cls the former class
     * @return true if the source is newer, false else
     * @throws IOException if it is not possible to open an
     * connection for the given source
     */
    protected boolean isSourceNewer(URL source, Class cls) throws IOException {
        long lastMod;

        // Special handling for file:// protocol, as getLastModified() often reports
        // incorrect results (-1)
        if (source.getProtocol().equals("file")) {
            // Coerce the file URL to a File
            String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
            File file = new File(path);
            lastMod = file.lastModified();
        }
        else {
            lastMod = source.openConnection().getLastModified();
        }
        long classTime = getTimeStamp(cls);
        return classTime+config.getMinimumRecompilationInterval() < lastMod;
    }

    /**
     * adds a classpath to this classloader.  
     * @param path is a jar file or a directory.
     * @see #addURL(URL)
     */
    public void addClasspath(final String path) {
        AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                try {
                    File f = new File(path);
                    URL newURL = f.toURI().toURL();
                    URL[] urls = getURLs();
                    for (int i=0; i<urls.length; i++) {
                        if (urls[i].equals(newURL)) return null;
                    }
                    addURL(newURL);
                } catch (MalformedURLException e) {
                    //TODO: fail through ?
                }
                return null;
            }
        });
    }

    /**
     * <p>Returns all Groovy classes loaded by this class loader.
     *
     * @return all classes loaded by this class loader
     */
    public Class[] getLoadedClasses() {
        synchronized (classCache) {
            return (Class[]) classCache.values().toArray(new Class[0]);
        }
    }
    
    /**
     * removes all classes from the class cache.
     * @see #getClassCacheEntry(String)
     * @see #setClassCacheEntry(Class)
     * @see #removeClassCacheEntry(String)
     */    
    public void clearCache() {
        synchronized (classCache) {
            classCache.clear();
        }
    }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?