diff --git a/src/helma/framework/core/AppClassLoader.java b/src/helma/framework/core/AppClassLoader.java
new file mode 100644
index 00000000..b2ba2888
--- /dev/null
+++ b/src/helma/framework/core/AppClassLoader.java
@@ -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;
+ }
+
+}
diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java
index 25a5ddfa..ba49c454 100644
--- a/src/helma/framework/core/Application.java
+++ b/src/helma/framework/core/Application.java
@@ -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 ) {
diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java
index 4270a1d0..64d2e043 100644
--- a/src/helma/framework/core/RequestEvaluator.java
+++ b/src/helma/framework/core/RequestEvaluator.java
@@ -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)
diff --git a/src/helma/framework/core/TypeManager.java b/src/helma/framework/core/TypeManager.java
index 60b329ba..0ae4710b 100644
--- a/src/helma/framework/core/TypeManager.java
+++ b/src/helma/framework/core/TypeManager.java
@@ -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.
@@ -31,6 +33,9 @@ public final class TypeManager {
Prototype hopobjectProto;
// 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"};
@@ -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]);
diff --git a/src/helma/main/HelmaSecurityManager.java b/src/helma/main/HelmaSecurityManager.java
new file mode 100644
index 00000000..e617e4ad
--- /dev/null
+++ b/src/helma/main/HelmaSecurityManager.java
@@ -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 getApplication
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 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 ();
diff --git a/src/helma/main/launcher/FilteredClassLoader.java b/src/helma/main/launcher/FilteredClassLoader.java
new file mode 100644
index 00000000..a6587bdd
--- /dev/null
+++ b/src/helma/main/launcher/FilteredClassLoader.java
@@ -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);
+ }
+}
diff --git a/src/helma/main/launcher/Main.java b/src/helma/main/launcher/Main.java
index 96abb1fc..d3b62dcc 100644
--- a/src/helma/main/launcher/Main.java
+++ b/src/helma/main/launcher/Main.java
@@ -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 {
- // try to get Helma installation directory
- 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);
- 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);
- }
- } catch (Exception ignore) {
- // unable to get Helma home dir from launcher jar
+ // 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!/{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());
+ 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
+ }
+ }
+ // 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