/*
* Copyright (C) 2000 - 2010 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfx;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.CodeSource;
import java.security.SecureClassLoader;
import java.util.Hashtable;
import com.naryx.tagfusion.cfm.engine.cfEngine;
/**
* CFXClassLoader
*
* Java CFX tags need to be loaded in a separate classloader so that the class
* files will get reloaded if they change. Also, this mimics the behavior of
* CF5/MX regarding static class attributes (creating a new classloader every
* time the tag is invoked causes the static attributes to be reset every time).
*/
public class CFXClassLoader extends SecureClassLoader {
private String tagsFolder;
// Cache classes
private Hashtable classCache = new Hashtable();
private Hashtable resolvedCache = new Hashtable();
public CFXClassLoader(ClassLoader parent, String tagsFolder) {
super(parent);
if (tagsFolder.endsWith(File.separator))
this.tagsFolder = tagsFolder;
else
this.tagsFolder = tagsFolder + File.separator;
}
public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class c = null;
// See if we have already cached this class
// If so don't return yet because we may need to resolve it.
c = (Class) classCache.get(name);
// If it isn't cached then look for it in the tags folder and then delegate
// to the parent classloader
if (c == null) {
// Try loading it from the tags folder if it's not a system class
if (!name.startsWith("java.") && !name.startsWith("sun.") && !name.startsWith("javax.") && !name.startsWith("com.allaire") && !name.startsWith("com.nary")) {
c = loadTagsFolderClass(name);
}
if (c == null) {
// Delegate to the parent classloader
try {
if (getParent() == null)
c = findSystemClass(name);
else
c = getParent().loadClass(name);
if (c == null)
throw new ClassNotFoundException(name + ", tags folder=" + tagsFolder);
classCache.put(name, c);
} catch (ClassNotFoundException e) {
throw new ClassNotFoundException(name + ", tags folder=" + tagsFolder);
} catch (Exception ex) {
throw new ClassNotFoundException(name + ", " + ex.toString() + ", tags folder=" + tagsFolder);
}
}
}
// If resolve is true and we haven't already resolved this class then call
// resolveClass
if ((resolve) && (!resolvedCache.containsKey(name))) {
resolveClass(c);
resolvedCache.put(name, "true");
}
return c;
}
private Class loadTagsFolderClass(String name) {
byte[] cdata = null;
URL url = null;
// Convert name from java.util.date to java/util/date
String pathname = tagsFolder + name.replace('.', File.separatorChar) + ".class";
// See if the class is in a .class file
// NOTE: if tagsFolder is a relative path then with BD Server and JX the
// getResolvedFile() method will resolve the path relative to the
// built-in web server's wwwroot directory.
File f = cfEngine.getResolvedFile(pathname);
if ((f != null) && f.exists()) {
// Load the class file
cdata = loadClassFile(name, f);
try {
url = new URL("file:" + pathname.replace('\\', '/'));
} catch (MalformedURLException e) {
com.nary.Debug.printStackTrace(e);
}
} else if (pathname.startsWith("/")) // for packed WARs
{
try {
url = cfEngine.thisServletContext.getResource(pathname.replace('\\', '/'));
if (url != null) {
URLConnection conn = url.openConnection();
cdata = loadClassFile(conn.getInputStream(), conn.getContentLength());
}
} catch (Exception e) {
com.nary.Debug.printStackTrace(e);
}
}
try {
// Cache and return the class
if (cdata != null) {
Class c;
if (url != null)
c = defineClass(name, cdata, 0, cdata.length, new CodeSource(url, (java.security.cert.Certificate[]) null));
else
c = defineClass(name, cdata, 0, cdata.length);
classCache.put(name, c);
return c;
}
} catch (ClassFormatError e) {
com.nary.Debug.printStackTrace(e);
}
// Return null if the class isn't in the tags folder
return null;
}
private byte[] loadClassFile(String name, File f) {
if (f.canRead() && (f.length() > 0)) {
try {
return loadClassFile(new FileInputStream(f), (int) f.length());
} catch (IOException e) {
com.nary.Debug.printStackTrace(e);
}
}
return null;
}
private byte[] loadClassFile(InputStream in, int len) {
if (len <= 0)
return null;
try {
byte[] cdata = new byte[len];
int left = len;
int num = 0;
while (left > 0) {
num = in.read(cdata, len - left, left);
if (num == -1)
break;
left -= num;
}
in.close();
return cdata;
} catch (IOException e) {
com.nary.Debug.printStackTrace(e);
return null;
}
}
}