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 + -
显示快捷键?