add: support for importing code using require() method (commonjs)

This commit is contained in:
Tobi Schäfer 2020-03-21 14:10:12 +01:00
parent db8d239c32
commit 23fdb31348
6 changed files with 475 additions and 152 deletions

View file

@ -46,11 +46,12 @@ configurations {
}
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.sun.activation:javax.activation:1.2.0'
implementation 'commons-codec:commons-codec:1.14'
implementation 'commons-fileupload:commons-fileupload:1.4'
implementation 'commons-logging:commons-logging:1.2'
implementation 'commons-net:commons-net:3.6'
implementation 'com.sun.activation:javax.activation:1.2.0'
implementation 'javax.mail:javax.mail-api:1.6.2'
implementation 'javax.servlet:javax.servlet-api:4.0.1'
implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'

View file

@ -7,31 +7,30 @@
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.main;
import helma.framework.core.*;
import helma.framework.repository.Repository;
import helma.framework.repository.FileRepository;
import helma.util.StringUtils;
import org.apache.xmlrpc.XmlRpcHandler;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.xmlrpc.XmlRpcHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import java.io.*;
import java.util.*;
import helma.util.ResourceProperties;
import helma.framework.core.Application;
import helma.framework.repository.FileRepository;
import helma.framework.repository.Repository;
import helma.servlet.EmbeddedServletClient;
import helma.util.ResourceProperties;
import helma.util.StringUtils;
/**
* This class is responsible for starting and stopping Helma applications.
@ -55,11 +54,11 @@ public class ApplicationManager implements XmlRpcHandler {
public ApplicationManager(ResourceProperties props, Server server) {
this.props = props;
this.server = server;
descriptors = new Hashtable();
applications = new Hashtable();
xmlrpcHandlers = new Hashtable();
lastModified = 0;
jetty = server.jetty;
this.descriptors = new Hashtable();
this.applications = new Hashtable();
this.xmlrpcHandlers = new Hashtable();
this.lastModified = 0;
this.jetty = server.jetty;
}
/**
@ -67,13 +66,13 @@ public class ApplicationManager implements XmlRpcHandler {
* to create and start new applications.
*/
protected void checkForChanges() {
if (props.lastModified() > lastModified && server.getApplicationsOption() == null) {
if (this.props.lastModified() > this.lastModified && this.server.getApplicationsOption() == null) {
try {
for (Enumeration e = props.keys(); e.hasMoreElements();) {
for (Enumeration e = this.props.keys(); e.hasMoreElements();) {
String appName = (String) e.nextElement();
if ((appName.indexOf(".") == -1) &&
(applications.get(appName) == null)) {
if ((appName.indexOf(".") == -1) && //$NON-NLS-1$
(this.applications.get(appName) == null)) {
AppDescriptor appDesc = new AppDescriptor(appName);
appDesc.start();
appDesc.bind();
@ -81,27 +80,27 @@ public class ApplicationManager implements XmlRpcHandler {
}
// then stop deleted ones
for (Enumeration e = descriptors.elements(); e.hasMoreElements();) {
for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) {
AppDescriptor appDesc = (AppDescriptor) e.nextElement();
// check if application has been removed and should be stopped
if (!props.containsKey(appDesc.appName)) {
if (!this.props.containsKey(appDesc.appName)) {
appDesc.stop();
} else if (server.jetty != null) {
} else if (this.server.jetty != null) {
// If application continues to run, remount
// as the mounting options may have changed.
AppDescriptor ndesc = new AppDescriptor(appDesc.appName);
ndesc.app = appDesc.app;
appDesc.unbind();
ndesc.bind();
descriptors.put(ndesc.appName, ndesc);
this.descriptors.put(ndesc.appName, ndesc);
}
}
} catch (Exception mx) {
getLogger().error("Error checking applications", mx);
}
lastModified = System.currentTimeMillis();
this.lastModified = System.currentTimeMillis();
}
}
@ -118,7 +117,7 @@ public class ApplicationManager implements XmlRpcHandler {
* Bind an application by name
*/
public void register(String appName) {
AppDescriptor desc = (AppDescriptor) descriptors.get(appName);
AppDescriptor desc = (AppDescriptor) this.descriptors.get(appName);
if (desc != null) {
desc.bind();
}
@ -128,7 +127,7 @@ public class ApplicationManager implements XmlRpcHandler {
* Stop an application by name
*/
public void stop(String appName) {
AppDescriptor desc = (AppDescriptor) descriptors.get(appName);
AppDescriptor desc = (AppDescriptor) this.descriptors.get(appName);
if (desc != null) {
desc.stop();
}
@ -140,18 +139,18 @@ public class ApplicationManager implements XmlRpcHandler {
*/
public void startAll() {
try {
String[] apps = server.getApplicationsOption();
String[] apps = this.server.getApplicationsOption();
if (apps != null) {
for (int i = 0; i < apps.length; i++) {
AppDescriptor desc = new AppDescriptor(apps[i]);
desc.start();
}
} else {
for (Enumeration e = props.keys(); e.hasMoreElements();) {
for (Enumeration e = this.props.keys(); e.hasMoreElements();) {
String appName = (String) e.nextElement();
if (appName.indexOf(".") == -1) {
String appValue = props.getProperty(appName);
if (appName.indexOf(".") == -1) { //$NON-NLS-1$
String appValue = this.props.getProperty(appName);
if (appValue != null && appValue.length() > 0) {
appName = appValue;
@ -163,12 +162,12 @@ public class ApplicationManager implements XmlRpcHandler {
}
}
for (Enumeration e = descriptors.elements(); e.hasMoreElements();) {
for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) {
AppDescriptor appDesc = (AppDescriptor) e.nextElement();
appDesc.bind();
}
lastModified = System.currentTimeMillis();
this.lastModified = System.currentTimeMillis();
} catch (Exception mx) {
getLogger().error("Error starting applications", mx);
mx.printStackTrace();
@ -179,7 +178,7 @@ public class ApplicationManager implements XmlRpcHandler {
* Stop all running applications.
*/
public void stopAll() {
for (Enumeration en = descriptors.elements(); en.hasMoreElements();) {
for (Enumeration en = this.descriptors.elements(); en.hasMoreElements();) {
try {
AppDescriptor appDesc = (AppDescriptor) en.nextElement();
@ -194,14 +193,14 @@ public class ApplicationManager implements XmlRpcHandler {
* Get an array containing all currently running applications.
*/
public Object[] getApplications() {
return applications.values().toArray();
return this.applications.values().toArray();
}
/**
* Get an application by name.
*/
public Application getApplication(String name) {
return (Application) applications.get(name);
return (Application) this.applications.get(name);
}
/**
@ -209,7 +208,7 @@ public class ApplicationManager implements XmlRpcHandler {
*/
public Object execute(String method, Vector params)
throws Exception {
int dot = method.indexOf(".");
int dot = method.indexOf("."); //$NON-NLS-1$
if (dot == -1) {
throw new Exception("Method name \"" + method +
@ -222,10 +221,10 @@ public class ApplicationManager implements XmlRpcHandler {
String handler = method.substring(0, dot);
String method2 = method.substring(dot + 1);
Application app = (Application) xmlrpcHandlers.get(handler);
Application app = (Application) this.xmlrpcHandlers.get(handler);
if (app == null) {
app = (Application) xmlrpcHandlers.get("*");
app = (Application) this.xmlrpcHandlers.get("*"); //$NON-NLS-1$
// use the original method name, the handler is resolved within the app.
method2 = method;
}
@ -240,33 +239,32 @@ public class ApplicationManager implements XmlRpcHandler {
private String getMountpoint(String mountpoint) {
mountpoint = mountpoint.trim();
if ("".equals(mountpoint)) {
return "/";
} else if (!mountpoint.startsWith("/")) {
return "/" + mountpoint;
if ("".equals(mountpoint)) { //$NON-NLS-1$
return "/"; //$NON-NLS-1$
} else if (!mountpoint.startsWith("/")) { //$NON-NLS-1$
return "/" + mountpoint; //$NON-NLS-1$
}
return mountpoint;
}
private String joinMountpoint(String prefix, String suffix) {
if (prefix.endsWith("/") || suffix.startsWith("/")) {
if (prefix.endsWith("/") || suffix.startsWith("/")) { //$NON-NLS-1$//$NON-NLS-2$
return prefix+suffix;
} else {
return prefix+"/"+suffix;
}
return prefix+"/"+suffix; //$NON-NLS-1$
}
private String getPathPattern(String mountpoint) {
if (!mountpoint.startsWith("/")) {
mountpoint = "/"+mountpoint;
if (!mountpoint.startsWith("/")) { //$NON-NLS-1$
mountpoint = "/"+mountpoint; //$NON-NLS-1$
}
if ("/".equals(mountpoint)) {
return "/";
if ("/".equals(mountpoint)) { //$NON-NLS-1$
return "/"; //$NON-NLS-1$
}
if (mountpoint.endsWith("/")) {
if (mountpoint.endsWith("/")) { //$NON-NLS-1$
return mountpoint.substring(0, mountpoint.length()-1);
}
@ -279,19 +277,18 @@ public class ApplicationManager implements XmlRpcHandler {
File file = new File(path);
if (file.isAbsolute()) {
return file;
} else {
return file.getAbsoluteFile();
}
return file.getAbsoluteFile();
}
private Log getLogger() {
return server.getLogger();
return this.server.getLogger();
}
private String findResource(String path) {
File file = new File(path);
if (!file.isAbsolute() && !file.exists()) {
file = new File(server.getHopHome(), path);
file = new File(this.server.getHopHome(), path);
}
return file.getAbsolutePath();
}
@ -336,58 +333,58 @@ public class ApplicationManager implements XmlRpcHandler {
* @param name the application name
*/
AppDescriptor(String name) {
ResourceProperties conf = props.getSubProperties(name + '.');
appName = name;
mountpoint = getMountpoint(conf.getProperty("mountpoint", appName));
pathPattern = getPathPattern(mountpoint);
staticDir = conf.getProperty("static");
staticMountpoint = getPathPattern(conf.getProperty("staticMountpoint",
joinMountpoint(mountpoint, "static")));
staticIndex = "true".equalsIgnoreCase(conf.getProperty("staticIndex"));
String home = conf.getProperty("staticHome");
ResourceProperties conf = ApplicationManager.this.props.getSubProperties(name + '.');
this.appName = name;
this.mountpoint = getMountpoint(conf.getProperty("mountpoint", this.appName)); //$NON-NLS-1$
this.pathPattern = getPathPattern(this.mountpoint);
this.staticDir = conf.getProperty("static"); //$NON-NLS-1$
this.staticMountpoint = getPathPattern(conf.getProperty("staticMountpoint", //$NON-NLS-1$
joinMountpoint(this.mountpoint, "static"))); //$NON-NLS-1$
this.staticIndex = "true".equalsIgnoreCase(conf.getProperty("staticIndex")); //$NON-NLS-1$//$NON-NLS-2$
String home = conf.getProperty("staticHome"); //$NON-NLS-1$
if (home == null) {
staticHome = new String[] {"index.html", "index.htm"};
this.staticHome = new String[] {"index.html", "index.htm"}; //$NON-NLS-1$ //$NON-NLS-2$
} else {
staticHome = StringUtils.split(home, ",");
this.staticHome = StringUtils.split(home, ","); //$NON-NLS-1$
}
protectedStaticDir = conf.getProperty("protectedStatic");
this.protectedStaticDir = conf.getProperty("protectedStatic"); //$NON-NLS-1$
cookieDomain = conf.getProperty("cookieDomain");
sessionCookieName = conf.getProperty("sessionCookieName");
protectedSessionCookie = conf.getProperty("protectedSessionCookie");
uploadLimit = conf.getProperty("uploadLimit");
uploadSoftfail = conf.getProperty("uploadSoftfail");
debug = conf.getProperty("debug");
String appDirName = conf.getProperty("appdir");
appDir = (appDirName == null) ? null : getAbsoluteFile(appDirName);
String dbDirName = conf.getProperty("dbdir");
dbDir = (dbDirName == null) ? null : getAbsoluteFile(dbDirName);
servletClassName = conf.getProperty("servletClass");
this.cookieDomain = conf.getProperty("cookieDomain"); //$NON-NLS-1$
this.sessionCookieName = conf.getProperty("sessionCookieName"); //$NON-NLS-1$
this.protectedSessionCookie = conf.getProperty("protectedSessionCookie"); //$NON-NLS-1$
this.uploadLimit = conf.getProperty("uploadLimit"); //$NON-NLS-1$
this.uploadSoftfail = conf.getProperty("uploadSoftfail"); //$NON-NLS-1$
this.debug = conf.getProperty("debug"); //$NON-NLS-1$
String appDirName = conf.getProperty("appdir"); //$NON-NLS-1$
this.appDir = (appDirName == null) ? null : getAbsoluteFile(appDirName);
String dbDirName = conf.getProperty("dbdir"); //$NON-NLS-1$
this.dbDir = (dbDirName == null) ? null : getAbsoluteFile(dbDirName);
this.servletClassName = conf.getProperty("servletClass"); //$NON-NLS-1$
// got ignore dirs
ignoreDirs = conf.getProperty("ignore");
this.ignoreDirs = conf.getProperty("ignore"); //$NON-NLS-1$
// read and configure app repositories
ArrayList repositoryList = new ArrayList();
Class[] parameters = { String.class };
for (int i = 0; true; i++) {
String repositoryArgs = conf.getProperty("repository." + i);
String repositoryArgs = conf.getProperty("repository." + i); //$NON-NLS-1$
if (repositoryArgs != null) {
// lookup repository implementation
String repositoryImpl = conf.getProperty("repository." + i +
".implementation");
String repositoryImpl = conf.getProperty("repository." + i + //$NON-NLS-1$
".implementation"); //$NON-NLS-1$
if (repositoryImpl == null) {
// implementation not set manually, have to guess it
if (repositoryArgs.endsWith(".zip")) {
if (repositoryArgs.endsWith(".zip")) { //$NON-NLS-1$
repositoryArgs = findResource(repositoryArgs);
repositoryImpl = "helma.framework.repository.ZipRepository";
} else if (repositoryArgs.endsWith(".js")) {
repositoryImpl = "helma.framework.repository.ZipRepository"; //$NON-NLS-1$
} else if (repositoryArgs.endsWith(".js")) { //$NON-NLS-1$
repositoryArgs = findResource(repositoryArgs);
repositoryImpl = "helma.framework.repository.SingleFileRepository";
repositoryImpl = "helma.framework.repository.SingleFileRepository"; //$NON-NLS-1$
} else {
repositoryArgs = findResource(repositoryArgs);
repositoryImpl = "helma.framework.repository.FileRepository";
repositoryImpl = "helma.framework.repository.FileRepository"; //$NON-NLS-1$
}
}
@ -408,17 +405,17 @@ public class ApplicationManager implements XmlRpcHandler {
}
}
if (appDir != null) {
FileRepository appRep = new FileRepository(appDir);
if (this.appDir != null) {
FileRepository appRep = new FileRepository(this.appDir);
if (!repositoryList.contains(appRep)) {
repositoryList.add(appRep);
}
} else if (repositoryList.isEmpty()) {
repositoryList.add(new FileRepository(
new File(server.getAppsHome(), appName)));
new File(ApplicationManager.this.server.getAppsHome(), this.appName)));
}
this.repositories = (RepositoryInterface[]) repositoryList.toArray(new RepositoryInterface[repositoryList.size()]);
this.repositories = (Repository[]) repositoryList.toArray(new Repository[repositoryList.size()]);
}
@ -427,21 +424,21 @@ public class ApplicationManager implements XmlRpcHandler {
try {
// create the application instance
app = new Application(appName, server, repositories, appDir, dbDir);
this.app = new Application(this.appName, ApplicationManager.this.server, this.repositories, this.appDir, this.dbDir);
// register ourselves
descriptors.put(appName, this);
applications.put(appName, app);
ApplicationManager.this.descriptors.put(this.appName, this);
ApplicationManager.this.applications.put(this.appName, this.app);
// the application is started later in the register method, when it's bound
app.init(ignoreDirs);
this.app.init(this.ignoreDirs);
// set application URL prefix if it isn't set in app.properties
if (!app.hasExplicitBaseURI()) {
app.setBaseURI(mountpoint);
if (!this.app.hasExplicitBaseURI()) {
this.app.setBaseURI(this.mountpoint);
}
app.start();
this.app.start();
} catch (Exception x) {
getLogger().error("Error creating application " + appName, x);
x.printStackTrace();
@ -462,8 +459,8 @@ public class ApplicationManager implements XmlRpcHandler {
getLogger().error("Couldn't stop app", x);
}
descriptors.remove(appName);
applications.remove(appName);
ApplicationManager.this.descriptors.remove(this.appName);
ApplicationManager.this.applications.remove(this.appName);
}
void bind() {
@ -471,21 +468,21 @@ public class ApplicationManager implements XmlRpcHandler {
getLogger().info("Binding application " + appName + " :: " + app.hashCode() + " :: " + this.hashCode());
// set application URL prefix if it isn't set in app.properties
if (!app.hasExplicitBaseURI()) {
app.setBaseURI(mountpoint);
if (!this.app.hasExplicitBaseURI()) {
this.app.setBaseURI(this.mountpoint);
}
// bind to Jetty HTTP server
if (jetty != null) {
if (context == null) {
context = new ContextHandlerCollection();
jetty.getHttpServer().setHandler(context);
if (ApplicationManager.this.jetty != null) {
if(ApplicationManager.this.context == null) {
ApplicationManager.this.context = new ContextHandlerCollection();
ApplicationManager.this.jetty.getHttpServer().setHandler(ApplicationManager.this.context);
}
// if there is a static direcory specified, mount it
if (staticDir != null) {
if (this.staticDir != null) {
File staticContent = getAbsoluteFile(staticDir);
File staticContent = getAbsoluteFile(this.staticDir);
getLogger().info("Serving static from " + staticContent.getPath());
getLogger().info("Mounting static at " + staticMountpoint);
@ -494,58 +491,60 @@ public class ApplicationManager implements XmlRpcHandler {
rhandler.setResourceBase(staticContent.getPath());
rhandler.setWelcomeFiles(staticHome);
staticContext = context.addContext(staticMountpoint, "");
staticContext = ApplicationManager.this.context.addContext(staticMountpoint, ""); //$NON-NLS-1$
staticContext.setHandler(rhandler);
staticContext.start();
}
appContext = new ServletContextHandler(context, pathPattern, true, true);
appContext = new ServletContextHandler(context, pathPattern);
Class servletClass = servletClassName == null ?
EmbeddedServletClient.class : Class.forName(servletClassName);
ServletHolder holder = new ServletHolder(servletClass);
holder.setInitParameter("application", appName);
appContext.addServlet(holder, "/*");
appContext.addServlet(holder, "/*"); //$NON-NLS-1$
if (cookieDomain != null) {
holder.setInitParameter("cookieDomain", cookieDomain);
holder.setInitParameter("application", appName); //$NON-NLS-1$
if (this.cookieDomain != null) {
holder.setInitParameter("cookieDomain", this.cookieDomain); //$NON-NLS-1$
}
if (sessionCookieName != null) {
holder.setInitParameter("sessionCookieName", sessionCookieName);
if (this.sessionCookieName != null) {
holder.setInitParameter("sessionCookieName", this.sessionCookieName); //$NON-NLS-1$
}
if (protectedSessionCookie != null) {
holder.setInitParameter("protectedSessionCookie", protectedSessionCookie);
if (this.protectedSessionCookie != null) {
holder.setInitParameter("protectedSessionCookie", this.protectedSessionCookie); //$NON-NLS-1$
}
if (uploadLimit != null) {
holder.setInitParameter("uploadLimit", uploadLimit);
if (this.uploadLimit != null) {
holder.setInitParameter("uploadLimit", this.uploadLimit); //$NON-NLS-1$
}
if (uploadSoftfail != null) {
holder.setInitParameter("uploadSoftfail", uploadSoftfail);
if (this.uploadSoftfail != null) {
holder.setInitParameter("uploadSoftfail", this.uploadSoftfail); //$NON-NLS-1$
}
if (debug != null) {
holder.setInitParameter("debug", debug);
if (this.debug != null) {
holder.setInitParameter("debug", this.debug); //$NON-NLS-1$
}
if (protectedStaticDir != null) {
File protectedContent = getAbsoluteFile(protectedStaticDir);
appContext.setResourceBase(protectedContent.getPath());
if (this.protectedStaticDir != null) {
File protectedContent = getAbsoluteFile(this.protectedStaticDir);
this.appContext.setResourceBase(protectedContent.getPath());
getLogger().info("Serving protected static from " +
protectedContent.getPath());
}
// Remap the context paths and start
context.mapContexts();
appContext.start();
ApplicationManager.this.context.mapContexts();
this.appContext.start();
}
// register as XML-RPC handler
xmlrpcHandlerName = app.getXmlRpcHandlerName();
xmlrpcHandlers.put(xmlrpcHandlerName, app);
this.xmlrpcHandlerName = this.app.getXmlRpcHandlerName();
ApplicationManager.this.xmlrpcHandlers.put(this.xmlrpcHandlerName, this.app);
} catch (Exception x) {
getLogger().error("Couldn't bind app", x);
x.printStackTrace();
@ -557,26 +556,26 @@ public class ApplicationManager implements XmlRpcHandler {
try {
// unbind from Jetty HTTP server
if (jetty != null) {
if (appContext != null) {
context.removeHandler(appContext);
appContext.stop();
appContext.destroy();
appContext = null;
if (ApplicationManager.this.jetty != null) {
if (this.appContext != null) {
ApplicationManager.this.context.removeHandler(this.appContext);
this.appContext.stop();
this.appContext.destroy();
this.appContext = null;
}
if (staticContext != null) {
context.removeHandler(staticContext);
staticContext.stop();
staticContext.destroy();
staticContext = null;
if (this.staticContext != null) {
ApplicationManager.this.context.removeHandler(this.staticContext);
this.staticContext.stop();
this.staticContext.destroy();
this.staticContext = null;
}
context.mapContexts();
ApplicationManager.this.context.mapContexts();
}
// unregister as XML-RPC handler
if (xmlrpcHandlerName != null) {
xmlrpcHandlers.remove(xmlrpcHandlerName);
if (this.xmlrpcHandlerName != null) {
ApplicationManager.this.xmlrpcHandlers.remove(this.xmlrpcHandlerName);
}
} catch (Exception x) {
getLogger().error("Couldn't unbind app", x);
@ -584,8 +583,9 @@ public class ApplicationManager implements XmlRpcHandler {
}
@Override
public String toString() {
return "[AppDescriptor "+app+"]";
return "[AppDescriptor "+this.app+"]"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}

View file

@ -0,0 +1,73 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 2017 Daniel Ruthardt. All rights reserved.
*/
package helma.scripting.rhino;
import java.net.URI;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.commonjs.module.ModuleScript;
import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProvider;
import org.mozilla.javascript.commonjs.module.provider.StrongCachingModuleScriptProvider;
/**
* Provides module scripts without compiling, should compiling not be possible for whatever reason.
* The main reason being targeted though, is the "generated bytecode for method exceeds 64K limit" issue.
*/
public class CompiledOrInterpretedModuleScriptProvider extends StrongCachingModuleScriptProvider {
/**
* Define the serialization UID.
*/
private static final long serialVersionUID = 1170789670529274963L;
/**
* Delegates to the super constructor.
*/
public CompiledOrInterpretedModuleScriptProvider(ModuleSourceProvider moduleSourceProvider) {
// do what would have been done anyways
super(moduleSourceProvider);
}
@Override
public ModuleScript getModuleScript(Context cx, String moduleId, URI moduleUri, URI baseUri, Scriptable paths) throws Exception {
try {
// try to load the module script with whatever optimization level is set for the application
return super.getModuleScript(cx, moduleId, moduleUri, baseUri, paths);
} catch (EvaluatorException ignore) {
// unlikely, but possible exception during loading the module script without compilation
Exception exception;
// get the application's optimization level
int optimizationLevel = cx.getOptimizationLevel();
try {
// set the optimization level to not compile, but interpret
cx.setOptimizationLevel(-1);
// load the module script with the newly set optimization level
ModuleScript moduleScript = super.getModuleScript(cx, moduleId, moduleUri, baseUri, paths);
// return the module script
return moduleScript;
} catch (Exception e) {
// remember the exception
exception = e;
} finally {
// re-set the optimization
cx.setOptimizationLevel(optimizationLevel);
}
// re-throw the exception catched when trying to load the module script without compilation
throw exception;
}
}
}

View file

@ -0,0 +1,35 @@
package helma.scripting.rhino;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import org.apache.commons.io.IOUtils;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
public class JSONModuleSource extends ModuleSource {
private static final long serialVersionUID = 4446798833357540398L;
public JSONModuleSource(Object securityDomain, URI uri, URI base, Object validator) {
super(null, securityDomain, uri, base, validator);
}
@Override
public Reader getReader() {
StringBuffer content = new StringBuffer();
content.append("module.exports = "); //$NON-NLS-1$
try {
content.append(IOUtils.toString(this.getUri().toURL().openStream()));
} catch (IOException e) {
content.append("null"); //$NON-NLS-1$
}
content.append(";"); //$NON-NLS-1$
return new StringReader(content.toString());
}
}

View file

@ -0,0 +1,212 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 2017 Daniel Ruthardt. All rights reserved.
*/
package helma.scripting.rhino;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import org.apache.commons.io.FilenameUtils;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
/**
* Bridges the gap between CommonJS-style module loading and Node.js-style module loading.
*/
public class NodeModulesProvider extends UrlModuleSourceProvider {
/**
* Define the serialization UID.
*/
private static final long serialVersionUID = 6858072487233136717L;
/**
* Delegates to the super constructor.
*/
public NodeModulesProvider(Iterable<URI> privilegedUris, Iterable<URI> fallbackUris) {
// do what would have been done anyways
super(privilegedUris, fallbackUris);
}
/**
* Do what Node.js's LOAD_AS_FILE(X) would do.
* Case 4 ("If X.node is a file, load X.node as binary addon. STOP") is currently not supported (for
* quite obvious reasons). We might want to load JAR files in the future.
*
* @see https://nodejs.org/dist/latest-v9.x/docs/api/modules.html#modules_file_modules
*
* @param uri
* The file to load. FILE, FILE.js and FILE.json will be tried in this order.
* @param base
* Not used, only forwarded to
* {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @param validator
* Not used, only forwarded to
* {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @return
* The module source or null, if the module was not found.
*
* @throws IOException
* See {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @throws URISyntaxException
* See {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
*/
private ModuleSource loadAsFile(URI uri, URI base, Object validator)
throws IOException, URISyntaxException {
// lets assume the module is a file
File file = new File(uri);
// check if the file exists and is a file
if (file.exists() && file.isFile()) {
// check if the file is a JSON file
if (file.getAbsolutePath().toLowerCase().endsWith(".json")) { //$NON-NLS-1$
// return a JSON module source
return new JSONModuleSource(null, file.toURI(), base, validator);
} else {
// do what would have been done anyways
return super.loadFromUri(uri, base, validator);
}
}
// lets assume the module is a JS file
file = new File(new File(uri).getPath() + ".js"); //$NON-NLS-1$
// check if a file.js exists and is a file
if (file.exists() && file.isFile()) {
// do what would have been done anyways
return super.loadFromUri(uri, base, validator);
}
// lets assume the module is a JSON file
file = new File(new File(uri).getPath() + ".json"); //$NON-NLS-1$
// check if a file.json exists and is a file
if (file.exists() && file.isFile()) {
// return a JSON module source
return new JSONModuleSource(null, file.toURI(), base, validator);
}
// module not found
return null;
}
/**
* Do what Node.js's LOAD_AS_DIRECTORY(X) would do.
*
* @see https://nodejs.org/dist/latest-v9.x/docs/api/modules.html#modules_file_modules
*
* @param uri
* The directory to load.
* @param base
* Not used, only forwarded to
* {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @param validator
* Not used, only forwarded to
* {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @return
* The module source or null, if the module was not found.
*
* @throws JsonSyntaxException
* Thrown for problems accessing the module's "package.json" file.
* @throws IOException
* See {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
* @throws URISyntaxException
* See {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}.
*/
private ModuleSource loadAsDirectory(URI uri, URI base, Object validator)
throws JsonSyntaxException, IOException, URISyntaxException {
// lets assume the module is a directory
File directory = new File(uri);
// check if the directory exists and is a directory
if (directory.exists() && directory.isDirectory()) {
// the module source
ModuleSource moduleSource;
// lets assume that there is a "package.json" file in the directory
File packageFile = new File(directory, "package.json"); //$NON-NLS-1$
// check if the there is a "package.json" file in the directory
if (packageFile.exists() && packageFile.isFile()) {
// parse the JSON file
JsonObject json = new JsonParser()
.parse(new String(Files.readAllBytes(packageFile.toPath()))).getAsJsonObject();
// check if the JSON file defines a main JS file
if (json.has("main")) { //$NON-NLS-1$
// get the main JS file, removing the filename extension
String main = FilenameUtils.removeExtension(json.get("main").getAsString()); //$NON-NLS-1$
// load as file
moduleSource = this.loadAsFile(new File(directory, main).toURI(), base, validator);
// check if something was loaded
if (moduleSource != null) {
// return the loaded module source
return moduleSource;
}
}
}
// load as index
moduleSource = this.loadAsFile(new File(directory, "index").toURI(), base, validator); //$NON-NLS-1$
// check if something was loaded
if (moduleSource != null) {
// return the loaded module source
return moduleSource;
}
}
// module not found
return null;
}
/**
* Do what Node.js's require(X) would do.
*
* Case 1 is not supported, you will have to use modules from npmjs.org, re-implementing the core
* core module's functionality. We might want to use Nodeschnaps in the future.
* Case 2 is not supported, paths are always treated as relative paths within the application's
* "commonjs" directory.
* Case 5 additionally tries {@link UrlModuleSourceProvider#loadSource(URI, URI, Object)}, even if it is
* very unlikely that something, which hasn't been tried yet, will be done. One could say we are just
* delegating throwing the error.
*
* @see https://nodejs.org/dist/latest-v9.x/docs/api/modules.html#modules_file_modules
* @see https://github.com/killmag10/nodeschnaps
*/
protected ModuleSource loadFromUri(URI uri, URI base, Object validator)
throws IOException, URISyntaxException {
// the module source
ModuleSource moduleSource;
// load as file
moduleSource = this.loadAsFile(uri, base, validator);
// check if something was loaded
if (moduleSource != null) {
// return the loaded module source
return moduleSource;
}
// load as directory
moduleSource = this.loadAsDirectory(uri, base, validator);
// check if something was loaded
if (moduleSource != null) {
// return the loaded module source
return moduleSource;
}
// do what would have been done anyways
return super.loadFromUri(uri, base, validator);
}
}

View file

@ -44,7 +44,7 @@ import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.WrapFactory;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.commonjs.module.RequireBuilder;
import org.mozilla.javascript.commonjs.module.provider.*;
import org.mozilla.javascript.commonjs.module.provider.StrongCachingModuleScriptProvider;
import org.mozilla.javascript.tools.debugger.ScopeProvider;
import java.io.*;
@ -175,9 +175,11 @@ public final class RhinoCore implements ScopeProvider {
}
}
// install the global require() function using our custom modules provider, so that
// CommonJS-style as well as NodeJS-style modules can be required
new RequireBuilder()
.setModuleScriptProvider(new StrongCachingModuleScriptProvider(
new UrlModuleSourceProvider(commonJsPaths, null)))
.setModuleScriptProvider(new CompiledOrInterpretedModuleScriptProvider(
new NodeModulesProvider(commonJsPaths, null)))
.setSandboxed(true)
.createRequire(context, global)
.install(global);