![JAR search and dependency download from the Maven repository](/logo.png)
org.luaj.vm2.server.LuajClassLoader Maven / Gradle / Ivy
Show all versions of luaj-jse Show documentation
/*******************************************************************************
* Copyright (c) 2015 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.server;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
/**
* Class loader that can be used to launch a lua script in a Java VM that has a
* unique set of classes for org.luaj classes.
*
* Note: This class is experimental and subject to change in future versions.
*
* By using a custom class loader per script, it allows the script to have
* its own set of globals, including static values such as shared metatables
* that cannot access lua values from other scripts because their classes are
* loaded from different class loaders. Thus normally unsafe libraries such
* as luajava can be exposed to scripts in a server environment using these
* techniques.
*
* All classes in the package "org.luaj.vm2." are considered user classes, and
* loaded into this class loader from their bytes in the class path. Other
* classes are considered systemc classes and loaded via the system loader. This
* class set can be extended by overriding {@link #isUserClass(String)}.
*
* The {@link Launcher} interface is loaded as a system class by exception so
* that the caller may use it to launch lua scripts.
*
* By default {@link #NewLauncher()} creates a subclass of {@link Launcher} of
* type {@link DefaultLauncher} which creates debug globals, runs the script,
* and prints the return values. This behavior can be changed by supplying a
* different implementation class to {@link #NewLauncher(Class)} which must
* extend {@link Launcher}.
*
* @see Launcher
* @see #NewLauncher()
* @see #NewLauncher(Class)
* @see DefaultLauncher
* @since luaj 3.0.1
*/
public class LuajClassLoader extends ClassLoader {
/** String describing the luaj packages to consider part of the user classes */
static final String luajPackageRoot = "org.luaj.vm2.";
/** String describing the Launcher interface to be considered a system class */
static final String launcherInterfaceRoot = Launcher.class.getName();
/** Local cache of classes loaded by this loader. */
Map> classes = new HashMap>();
/**
* Construct a default {@link Launcher} instance that will load classes in
* its own {@link LuajClassLoader} using the default implementation class
* {@link DefaultLauncher}.
*
* The {@link Launcher} that is returned will be a pristine luaj vm
* whose classes are loaded into this loader including static variables
* such as shared metatables, and should not be able to directly access
* variables from other Launcher instances.
*
* @return {@link Launcher} instance that can be used to launch scripts.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public static Launcher NewLauncher() throws InstantiationException,
IllegalAccessException, ClassNotFoundException {
return NewLauncher(DefaultLauncher.class);
}
/**
* Construct a {@link Launcher} instance that will load classes in
* its own {@link LuajClassLoader} using a user-supplied implementation class
* that implements {@link Launcher}.
*
* The {@link Launcher} that is returned will be a pristine luaj vm
* whose classes are loaded into this loader including static variables
* such as shared metatables, and should not be able to directly access
* variables from other Launcher instances.
*
* @return instance of type 'launcher_class' that can be used to launch scripts.
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public static Launcher NewLauncher(Class extends Launcher> launcher_class)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
final LuajClassLoader loader = new LuajClassLoader();
final Object instance = loader.loadAsUserClass(launcher_class.getName())
.newInstance();
return (Launcher) instance;
}
/**
* Test if a class name should be considered a user class and loaded
* by this loader, or a system class and loaded by the system loader.
* @param classname Class name to test.
* @return true if this should be loaded into this class loader.
*/
public static boolean isUserClass(String classname) {
return classname.startsWith(luajPackageRoot)
&& !classname.startsWith(launcherInterfaceRoot);
}
public Class> loadClass(String classname) throws ClassNotFoundException {
if (classes.containsKey(classname))
return classes.get(classname);
if (!isUserClass(classname))
return super.findSystemClass(classname);
return loadAsUserClass(classname);
}
private Class> loadAsUserClass(String classname) throws ClassNotFoundException {
final String path = classname.replace('.', '/').concat(".class");
InputStream is = getResourceAsStream(path);
if (is != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
for (int n = 0; (n = is.read(b)) >= 0;)
baos.write(b, 0, n);
byte[] bytes = baos.toByteArray();
Class> result = super.defineClass(classname, bytes, 0,
bytes.length);
classes.put(classname, result);
return result;
} catch (java.io.IOException e) {
throw new ClassNotFoundException("Read failed: " + classname
+ ": " + e);
}
}
throw new ClassNotFoundException("Not found: " + classname);
}
}