merge in classloader branch

This commit is contained in:
hns 2002-11-21 15:55:32 +00:00
parent 75afb61744
commit 039ab180be
9 changed files with 319 additions and 64 deletions

View file

@ -0,0 +1,37 @@
// ApplicationClassLoader.java
package helma.framework.core;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
/**
* ClassLoader subclass with package accessible addURL method.
*/
public class AppClassLoader extends URLClassLoader {
private final String appname;
/**
* Create a HelmaClassLoader with the given application name and the given URLs
*/
public AppClassLoader(String appname, URL[] urls) {
super ( urls, AppClassLoader.class.getClassLoader());
this.appname = appname;
}
protected void addURL (URL url) {
super.addURL (url);
}
public String getAppName () {
return appname;
}
}

View file

@ -17,6 +17,7 @@ import org.apache.xmlrpc.*;
import java.util.*;
import java.io.*;
import java.net.URLEncoder;
import java.net.MalformedURLException;
import java.lang.reflect.*;
import java.rmi.*;
import java.rmi.server.*;
@ -122,13 +123,6 @@ public final class Application implements IPathElement, Runnable {
private String xmlrpcHandlerName;
/**
* Zero argument constructor needed for RMI
*/
/* public Application () throws RemoteException {
super ();
} */
/**
* Build an application with the given name in the app directory. No Server-wide
* properties are created or used.
@ -224,7 +218,7 @@ public final class Application implements IPathElement, Runnable {
/**
* Get the application ready to run, initializing the evaluators and type manager.
*/
public void init () throws DatabaseException, ScriptingException {
public void init () throws DatabaseException, ScriptingException, MalformedURLException {
// scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
// scriptingEngine.init (this, props);
@ -540,11 +534,11 @@ public final class Application implements IPathElement, Runnable {
if (rootObject == null) {
try {
if ( classMapping.containsKey("root.factory.class") && classMapping.containsKey("root.factory.method") ) {
Class c = Class.forName( classMapping.getProperty("root.factory.class") );
Class c = typemgr.loader.loadClass( classMapping.getProperty("root.factory.class") );
Method m = c.getMethod( classMapping.getProperty("root.factory.method"), null );
rootObject = m.invoke(c, null);
} else {
Class c = Class.forName( classMapping.getProperty("root") );
Class c = typemgr.loader.loadClass( classMapping.getProperty("root") );
rootObject = c.newInstance();
}
} catch ( Exception e ) {

View file

@ -72,13 +72,11 @@ public final class RequestEvaluator implements Runnable {
if (scriptingEngine == null) {
String engineClassName = app.getProperty (
"scripting.engine.factory",
"helma.scripting.fesi.FesiEngineFactory");
"helma.scripting.fesi.FesiEngine");
try {
Class clazz = Class.forName (engineClassName);
Class[] argClasses = {app.getClass(), getClass()};
Method method = clazz.getMethod ("getEngine", argClasses);
Object[] args = {app, this};
scriptingEngine = (ScriptingEngine) method.invoke (null, args);
Class clazz = app.typemgr.loader.loadClass (engineClassName);
scriptingEngine = (ScriptingEngine) clazz.newInstance ();
scriptingEngine.init (app, this);
} catch (Exception x) {
Throwable t = x;
if (x instanceof InvocationTargetException)

View file

@ -9,7 +9,8 @@ import helma.scripting.*;
import helma.util.*;
import java.util.*;
import java.io.*;
import java.net.URL;
import java.net.MalformedURLException;
/**
* The type manager periodically checks the prototype definitions for its
@ -20,8 +21,9 @@ public final class TypeManager {
Application app;
File appDir;
HashMap prototypes;
HashMap zipfiles;
HashMap prototypes; // map of prototypes
HashMap zipfiles; // map of zipped script files
HashSet jarfiles; // set of Java archives
long lastCheck = 0;
long appDirMod = 0;
// a checksum that changes whenever something in the application files changes.
@ -32,6 +34,9 @@ public final class TypeManager {
// the global prototype
Prototype globalProto;
// app specific class loader, includes jar files in the app directory
AppClassLoader loader;
final static String[] standardTypes = {"user", "global", "root", "hopobject"};
final static String templateExtension = ".hsp";
@ -39,7 +44,7 @@ public final class TypeManager {
final static String actionExtension = ".hac";
final static String skinExtension = ".skin";
public TypeManager (Application app) {
public TypeManager (Application app) throws MalformedURLException {
this.app = app;
appDir = app.appDir;
// make sure the directories for the standard prototypes exist, and lament otherwise
@ -54,6 +59,9 @@ public final class TypeManager {
}
prototypes = new HashMap ();
zipfiles = new HashMap ();
jarfiles = new HashSet ();
URL helmajar = new URL ("file:"+app.home.getAbsolutePath()+"/lib/helma.jar");
loader = new AppClassLoader(app.getName(), new URL[] { helmajar });
}
@ -111,6 +119,16 @@ public final class TypeManager {
}
continue;
}
if (list[i].endsWith (".jar")) {
if (!jarfiles.contains (list[i])) {
jarfiles.add (list[i]);
File f = new File (appDir, list[i]);
try {
loader.addURL (new URL ("file:"+f.getAbsolutePath()));
} catch (MalformedURLException ignore) {}
}
continue;
}
if (list[i].indexOf ('.') > -1)
continue;
Prototype proto = getPrototype (list[i]);

View file

@ -0,0 +1,94 @@
// HelmaSecurityManager.java
package helma.main;
import java.security.Permission;
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.util.HashSet;
import helma.framework.core.AppClassLoader;
/**
* Liberal security manager for Helma system that makes sure application code
* is not allowed to exit the VM and set a security manager.
*
* This class can be subclassed to implement actual security policies. It contains
* a utility method <code>getApplication</code> that can be used to determine
* the name of the application trying to execute the action in question, if any.
*/
public class HelmaSecurityManager extends SecurityManager {
// The set of actions forbidden to application code.
// We are pretty permissive, forbidding only System.exit()
// and setting the security manager.
private final static HashSet forbidden = new HashSet ();
static {
forbidden.add ("exitVM");
forbidden.add ("setSecurityManager");
}
public void checkPermission(Permission p) {
if (p instanceof RuntimePermission) {
if (forbidden.contains (p.getName())) {
Class[] classes = getClassContext();
for (int i=0; i<classes.length; i++) {
if (classes[i].getClassLoader() instanceof AppClassLoader)
throw new SecurityException (p.getName()+" not allowed for application code");
}
}
}
}
public void checkPermission(Permission p, Object context) {
}
public void checkCreateClassLoader() {}
public void checkAccess(Thread thread) {}
public void checkAccess(ThreadGroup group) {}
public void checkExit(int status) {
Class[] classes = getClassContext();
for (int i=0; i<classes.length; i++) {
if (classes[i].getClassLoader() instanceof AppClassLoader)
throw new SecurityException ("operation not allowed for application code");
}
}
public void checkExec(String cmd) {}
public void checkLink(String lib) {}
public void checkRead(FileDescriptor fdesc) {}
public void checkRead(String file) {}
public void checkRead(String file, Object context) {}
public void checkWrite(FileDescriptor fdesc) {}
public void checkWrite(String file) {}
public void checkDelete(String file) {}
public void checkConnect(String host, int port) {}
public void checkConnect(String host, int port, Object context) {}
public void checkListen(int port) {}
public void checkAccept(String host, int port) {}
public void checkMulticast(InetAddress addr) {}
public void checkMulticast(InetAddress addr, byte ttl) {}
public void checkPropertiesAccess() {}
public void checkPropertyAccess(String key) {}
public boolean checkTopLevelWindow(Object window) { return true; }
public void checkPrintJobAccess() {}
public void checkSystemClipboardAccess() {}
public void checkAwtEventQueueAccess() {}
public void checkPackageAccess(String pkg) {}
public void checkPackageDefinition(String pkg) {}
public void checkSetFactory() {}
public void checkMemberAccess(Class clazz, int which) {}
public void checkSecurityAccess(String target) {}
/**
* Utility method that returns the name of the application trying
* to execute the code in question. Returns null if the current code
* does not belong to any application.
*/
protected String getApplication () {
Class[] classes = getClassContext();
for (int i=0; i<classes.length; i++) {
if (classes[i].getClassLoader() instanceof AppClassLoader)
return ((AppClassLoader) classes[i].getClassLoader()).getAppName ();
}
// no application class loader found in stack - return null
return null;
}
}

View file

@ -15,11 +15,11 @@ import java.io.IOException;
* server-to-server.
*/
public class HopSocketFactory extends RMISocketFactory {
public class HelmaSocketFactory extends RMISocketFactory {
private InetAddressFilter filter;
public HopSocketFactory () {
public HelmaSocketFactory () {
filter = new InetAddressFilter ();
}

View file

@ -25,8 +25,8 @@ import org.apache.xmlrpc.*;
public class Server implements IPathElement, Runnable {
public static final String version = "1.2pre3+ 2002/10/24";
public long starttime;
public static final String version = "1.2pre3+ 2002/11/21";
public final long starttime;
// if true we only accept RMI and XML-RPC connections from
// explicitly listed hosts.
@ -141,13 +141,46 @@ import org.apache.xmlrpc.*;
usageError = true;
}
// get main property file from home dir or vice versa, depending on what we have.
// get property file from hopHome
if (propfile == null) {
if (homeDir != null)
propfile = new File (homeDir, "server.properties").getAbsolutePath ();
else
propfile = new File ("server.properties").getAbsolutePath ();
}
// create system properties
sysProps = new SystemProperties (propfile);
// check if there's a property setting for those ports not specified via command line
if (websrvPort == 0 && sysProps.getProperty ("webPort") != null) try {
websrvPort = Integer.parseInt (sysProps.getProperty ("webPort"));
} catch (NumberFormatException fmt) {
System.err.println ("Error parsing web server port property: "+fmt);
}
if (ajp13Port == 0 && sysProps.getProperty ("ajp13Port") != null) try {
ajp13Port = Integer.parseInt (sysProps.getProperty ("ajp13Port"));
} catch (NumberFormatException fmt) {
System.err.println ("Error parsing AJP1.3 server port property: "+fmt);
}
if (rmiPort == 0 && sysProps.getProperty ("rmiPort") != null) try {
rmiPort = Integer.parseInt (sysProps.getProperty ("rmiPort"));
} catch (NumberFormatException fmt) {
System.err.println ("Error parsing RMI server port property: "+fmt);
}
if (xmlrpcPort == 0 && sysProps.getProperty ("xmlrpcPort") != null) try {
xmlrpcPort = Integer.parseInt (sysProps.getProperty ("xmlrpcPort"));
} catch (NumberFormatException fmt) {
System.err.println ("Error parsing XML-RPC server port property: "+fmt);
}
// check server ports. If no port is set, issue a warning and exit.
if (!usageError && (websrvPort | ajp13Port | rmiPort | xmlrpcPort) == 0) {
System.out.println (" Error: No server port specified.");
usageError = true;
}
// if there's a usage error, output message and exit
if (usageError ) {
System.out.println ("usage: java helma.main.Server [-h dir] [-f file] [-p port] [-w port] [-x port]");
System.out.println (" -h dir Specify hop home directory");
@ -160,7 +193,7 @@ import org.apache.xmlrpc.*;
System.exit (0);
}
// check if servers are already running on the given ports
// check if any of the specified server ports is in use already
try {
if (websrvPort > 0)
checkRunning (websrvPort);
@ -175,18 +208,6 @@ import org.apache.xmlrpc.*;
System.exit (1);
}
// get main property file from home dir or vice versa, depending on what we have.
// get property file from hopHome
if (propfile == null) {
if (homeDir != null)
propfile = new File (homeDir, "server.properties").getAbsolutePath ();
else
propfile = new File ("server.properties").getAbsolutePath ();
}
// create system properties
sysProps = new SystemProperties (propfile);
// get hopHome from property file
if (homeDir == null)
homeDir = sysProps.getProperty ("hophome");
@ -343,7 +364,7 @@ import org.apache.xmlrpc.*;
if (rmiPort > 0) {
if (paranoid) {
HopSocketFactory factory = new HopSocketFactory ();
HelmaSocketFactory factory = new HelmaSocketFactory ();
String rallow = sysProps.getProperty ("allowWeb");
if (rallow != null) {
StringTokenizer st = new StringTokenizer (rallow, " ,;");
@ -370,6 +391,19 @@ import org.apache.xmlrpc.*;
return;
}
// set the security manager.
// the default implementation is helma.main.HelmaSecurityManager.
try {
String secManClass = sysProps.getProperty ("securityManager");
if (secManClass != null) {
SecurityManager secMan = (SecurityManager) Class.forName(secManClass).newInstance ();
System.setSecurityManager (secMan);
getLogger().log ("Setting security manager to "+secManClass);
}
} catch (Exception x) {
getLogger().log ("Error setting security manager: "+x);
}
// start applications
appManager.startAll ();

View file

@ -0,0 +1,36 @@
// FilteredClassLoader.java
package helma.main.launcher;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Hashtable;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.CodeSource;
/**
* ClassLoader that is able to exclude certain packages from loading.
*/
public class FilteredClassLoader extends URLClassLoader {
/**
* Create a server wide class loader that doesn't see the scripting engine(s)
* embedded in helma.jar. These files should be loaded by the per-application
* class loaders so that special security policies can be applied to them and
* so that they can load classes from jar files in the app directories.
*/
public FilteredClassLoader(URL[] urls) {
super (urls);
}
/**
* Mask classes that implement the scripting engine(s) contained in helma.jar
*/
protected Class findClass (String name) throws ClassNotFoundException {
if (name != null && (name.startsWith ("helma.scripting.fesi") || name.startsWith ("FESI")))
throw new ClassNotFoundException (name);
return super.findClass (name);
}
}

View file

@ -7,6 +7,8 @@ import java.net.URL;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.security.Policy;
/**
* Helma bootstrap class. Figures out Helma home directory, sets up class path and
@ -14,44 +16,86 @@ import java.lang.reflect.Method;
*/
public class Main {
public static final String[] jars = { "helma.jar", "jetty.jar", "crimson.jar", "xmlrpc.jar",
"village.jar", "servlet.jar", "regexp.jar", "mail.jar",
"activation.jar", "netcomponents.jar", "jimi.jar",
"apache-dom.jar", "jdom.jar"};
public static void main (String[] args) throws Exception {
// check if home directory is set via command line arg. If not,
// we'll get it from the location of the jar file this class
// has been loaded from.
String home = null;
// first, try to get helma home dir from command line options
for (int i=0; i<args.length; i++) {
if (args[i].equals ("-h") && i+1<args.length)
home = args[i+1];
}
URLClassLoader apploader = (URLClassLoader) ClassLoader.getSystemClassLoader();
// try to get Helma installation directory
if (home == null) {
try {
URLClassLoader apploader = (URLClassLoader) Main.class.getClassLoader();
// (URLClassLoader) Thread.currentThread().getContextClassLoader();
URL homeUrl = apploader.findResource("helma/main/launcher/Main.class");
String home = homeUrl.toString().substring(4);
// this is a JAR URL of the form
// jar:<url>!/{entry}
// we strip away the jar: prefix and the !/{entry} suffix
// to get the original jar file URL
home = homeUrl.toString().substring(4);
int excl = home.indexOf ("!");
if (excl > -1) {
home = home.substring(0, excl);
homeUrl = new URL (home);
File f = new File (homeUrl.getPath());
f = f.getParentFile();
System.err.println ("GOT HOME: "+f);
home = f.getParent();
// add home dir to the command line arguments
String[] newArgs = new String [args.length+2];
newArgs[0] = "-h";
newArgs[1] = home;
System.arraycopy (args, 0, newArgs, 2, args.length);
args = newArgs;
}
} catch (Exception ignore) {
// unable to get Helma home dir from launcher jar
}
File libdir = new File ("lib");
File[] files = libdir.listFiles (new FilenameFilter() {
}
// set the current working directory to the helma home dir.
// note that this is not a real cwd, which is not supported
// by java. It makes sure relative to absolute path name
// conversion is done right, so for Helma code, this should
// work.
System.setProperty ("user.dir", home);
// set up the class path
File libdir = new File (home, "lib");
ArrayList jarlist = new ArrayList ();
for (int i=0;i<jars.length;i++) {
File jar = new File (libdir, jars[i]);
if (jar.exists())
jarlist.add (new URL ("file:" + jar.getAbsolutePath()));
}
// add all jar files from the lib/ext directory
File extdir =new File (libdir, "ext");
File[] files = extdir.listFiles (new FilenameFilter() {
public boolean accept (File dir, String name) {
if (name.startsWith ("helma"))
return "helma.jar".equals (name);
return name.endsWith (".jar");
}
});
URL[] urls = new URL[files.length];
for (int i=0; i<files.length; i++) {
urls[i] = new URL ("file:"+files[i].getAbsolutePath());
}
URLClassLoader loader = new URLClassLoader (urls);
if (files != null)
for (int i=0;i<files.length; i++)
jarlist.add (new URL ("file:" + files[i].getAbsolutePath()));
URL[] urls = new URL[jarlist.size()];
jarlist.toArray (urls);
FilteredClassLoader loader = new FilteredClassLoader (urls);
// set the new class loader as context class loader
Thread.currentThread().setContextClassLoader (loader);
// get the main server class
Class clazz = loader.loadClass ("helma.main.Server");
Class[] cargs = new Class[] { args.getClass() };
Method main = clazz.getMethod ("main", cargs);
Object[] nargs = new Object[] { args };
// run
main.invoke (null, nargs);
}