1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-06 07:32:46 +00:00

Add Eclipse Java Compiler support for the scripting service

Adds support for the ECJ in the scripting service. Now it is possible to
choose which implementation to use to compile Java scripts.
This commit is contained in:
2011-10-10 14:15:54 -03:00
parent 8b787349d4
commit fe882c4467
18 changed files with 952 additions and 322 deletions

View File

@@ -217,5 +217,10 @@
<version>2.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>3.7.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,140 +0,0 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.compiled;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import com.l2jserver.service.game.scripting.ScriptClassLoader;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This classloader is used to load script classes. <br>
* <br>
* Due to JavaCompiler limitations we have to keep list of available classes
* here.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class PrecompiledScriptClassLoader extends ScriptClassLoader {
/**
* Map of all loaded classes
*/
private final Map<String, Class<?>> classes = CollectionFactory.newMap();
/**
* Creates new ScriptClassLoader with given ClassFileManger
*
* @param root
* the root file
*/
PrecompiledScriptClassLoader(final File root) {
super(new URL[] {});
}
/**
* Creates new ScriptClassLoader with given ClassFileManger and another
* classLoader as parent
*
* @param parent
* parent classLoader
*/
PrecompiledScriptClassLoader(ClassLoader parent) {
super(new URL[] {}, parent);
}
/**
* AddsLibrary jar
*
* @param file
* jar file to add
* @throws IOException
* if any I/O error occur
*/
@Override
public void addLibrary(File file) throws IOException {
}
/**
* Loads class from library, parent or compiled
*
* @param file
* the class file
* @return loaded class
* @throws ClassNotFoundException
* if class not found
* @throws IOException
* if any I/O error occur
*/
public Class<?> loadClass(File file) throws ClassNotFoundException,
IOException {
byte[] b = FileUtils.readFileToByteArray(file);
Class<?> c = super.defineClass(null, b, 0, b.length);
classes.put(c.getName(), c);
return c;
}
@Override
public synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
final File file = new File("target/templates");
final File classFile = new File(file, name.replaceAll("\\.", "/")
+ ".class");
System.out.println(name + " -> " + classFile);
if (!classFile.exists()) {
System.out.println("super");
return super.loadClass(name, resolve);
} else if (classes.containsKey(name)) {
return classes.get(name);
} else {
try {
byte[] b = FileUtils.readFileToByteArray(classFile);
System.out.println("Defining...");
Class<?> c = super.defineClass(null, b, 0, b.length);
classes.put(c.getName(), c);
System.out.println(c.getName());
return c;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getLibraryClasses() {
return Collections.emptySet();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getCompiledClasses() {
return Collections.unmodifiableSet(classes.keySet());
}
}

View File

@@ -1,96 +0,0 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.compiled;
import java.io.File;
import java.io.IOException;
import java.util.List;
import com.l2jserver.service.game.scripting.CompilationResult;
import com.l2jserver.service.game.scripting.ScriptClassLoader;
import com.l2jserver.service.game.scripting.ScriptCompiler;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Wrapper for JavaCompiler api
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class PrecompiledScriptCompiler implements ScriptCompiler {
/**
* Parent classloader that has to be used for this compiler
*/
protected ScriptClassLoader parentClassLoader;
/**
* Creates a new instance
*/
public PrecompiledScriptCompiler() {
}
@Override
public void setParentClassLoader(ScriptClassLoader classLoader) {
parentClassLoader = classLoader;
}
@Override
public void setLibraries(Iterable<File> files) {
// do nothing
}
@Override
public CompilationResult compile(String className, byte[] sourceCode) {
throw new UnsupportedOperationException(
"This compiler cannot compile from source");
}
@Override
public CompilationResult compile(String[] className, byte[][] sourceCode)
throws IllegalArgumentException {
throw new UnsupportedOperationException(
"This compiler cannot compile from source");
}
@Override
public CompilationResult compile(Iterable<File> compilationUnits) {
final PrecompiledScriptClassLoader cl = new PrecompiledScriptClassLoader(
this.getClass().getClassLoader());
final List<Class<?>> classes = CollectionFactory.newList();
for (final File file : compilationUnits) {
try {
classes.add(cl.loadClass(file));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return new CompilationResult(classes.toArray(new Class<?>[classes
.size()]), cl);
}
/**
* Only class files are supported by this compiler
*
* @return "class";
*/
@Override
public String[] getSupportedFileTypes() {
return new String[] { "class" };
}
}

View File

@@ -0,0 +1,219 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.ecj;
import java.io.File;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.tools.DiagnosticListener;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;
import com.l2jserver.service.game.scripting.ScriptClassLoader;
import com.l2jserver.service.game.scripting.impl.BinaryClass;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This class extends manages loaded classes. It is also responsible for
* tricking compiler. Unfortunally compiler doen't work with classloaders, so we
* have to pass class data manually for each compilation.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EclipseCompilerClassFileManager extends
ForwardingJavaFileManager<JavaFileManager> {
/**
* This map contains classes compiled for this classloader
*/
private final Map<String, BinaryClass> compiledClasses = CollectionFactory
.newMap();
/**
* Classloader that will be used to load compiled classes
*/
protected EclipseCompilerScriptClassLoader loader;
/**
* Parent classloader for loader
*/
protected ScriptClassLoader parentClassLoader;
/**
* Creates new ClassFileManager.
*
* @param compiler
* that will be used
* @param listener
* class that will report compilation errors
*/
public EclipseCompilerClassFileManager(JavaCompiler compiler,
DiagnosticListener<? super JavaFileObject> listener) {
super(compiler.getStandardFileManager(listener, null, null));
}
/**
* Returns JavaFileObject that will be used to write class data into it by
* compiler
*
* @param location
* not used
* @param className
* JavaFileObject will have this className
* @param kind
* not used
* @param sibling
* not used
* @return JavaFileObject that will be uesd to store compiled class data
* @throws IOException
* never thrown
*/
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, Kind kind, FileObject sibling) throws IOException {
BinaryClass co = new BinaryClass(className);
compiledClasses.put(className, co);
return co;
}
/**
* Returns classloaded of this ClassFileManager. If not exists, creates new
*
* @param location
* not used
* @return classLoader of this ClassFileManager
*/
@Override
public synchronized EclipseCompilerScriptClassLoader getClassLoader(Location location) {
if (loader == null) {
loader = AccessController
.doPrivileged(new PrivilegedAction<EclipseCompilerScriptClassLoader>() {
@Override
public EclipseCompilerScriptClassLoader run() {
if (parentClassLoader != null) {
return new EclipseCompilerScriptClassLoader(
EclipseCompilerClassFileManager.this,
EclipseCompilerClassFileManager.this.parentClassLoader);
} else {
return new EclipseCompilerScriptClassLoader(
EclipseCompilerClassFileManager.this);
}
}
});
}
return loader;
}
/**
* Sets paraentClassLoader for this classLoader
*
* @param classLoader
* parent class loader
*/
public synchronized void setParentClassLoader(ScriptClassLoader classLoader) {
this.parentClassLoader = classLoader;
}
/**
* Adds library file. Library file must be a .jar archieve
*
* @param file
* link to jar archieve
* @throws IOException
* if something goes wrong
*/
public void addLibrary(File file) throws IOException {
EclipseCompilerScriptClassLoader classLoader = getClassLoader(null);
classLoader.addLibrary(file);
}
/**
* Adds list of files as libraries. Files must be jar archieves
*
* @param files
* list of jar archives
* @throws IOException
* if some5thing goes wrong
*/
public void addLibraries(Iterable<File> files) throws IOException {
for (File f : files) {
addLibrary(f);
}
}
/**
* Returns list of classes that were compiled by conpiler related to this
* ClassFileManager
*
* @return list of classes
*/
public Map<String, BinaryClass> getCompiledClasses() {
return compiledClasses;
}
/**
* This method overrides class resolving procedure for compiler. It uses
* classloaders to resolve classes that compiler may need during
* compilation.
*
* Compiler by itself can't detect them. So we have to use this hack here.
*
* Hack is used only if compiler requests for classes in classpath.
*
* @param location
* Location to search classes
* @param packageName
* package to scan for classes
* @param kinds
* FileTypes to search
* @param recurse
* not used
* @return list of requered files
* @throws IOException
* if something foes wrong
*/
@Override
public synchronized Iterable<JavaFileObject> list(Location location,
String packageName, Set<Kind> kinds, boolean recurse)
throws IOException {
Iterable<JavaFileObject> objects = super.list(location, packageName,
kinds, recurse);
if (StandardLocation.CLASS_PATH.equals(location)
&& kinds.contains(Kind.CLASS)) {
List<JavaFileObject> temp = CollectionFactory.newList();
for (JavaFileObject object : objects) {
temp.add(object);
}
temp.addAll(loader.getClassesForPackage(packageName));
objects = temp;
}
return objects;
}
}

View File

@@ -0,0 +1,290 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.ecj;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.tools.JavaFileObject;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.service.game.scripting.ScriptClassLoader;
import com.l2jserver.service.game.scripting.impl.BinaryClass;
import com.l2jserver.util.ClassUtils;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This classloader is used to load script classes. <br>
* <br>
* Due to JavaCompiler limitations we have to keep list of available classes
* here.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EclipseCompilerScriptClassLoader extends ScriptClassLoader {
/**
* Logger
*/
private static final Logger log = LoggerFactory
.getLogger(EclipseCompilerScriptClassLoader.class);
/**
* URL Stream handler to allow valid url generation by
* {@link #getResource(String)}
*/
private final EclipseCompilerVirtualClassURLStreamHandler urlStreamHandler = new EclipseCompilerVirtualClassURLStreamHandler(
this);
/**
* ClassFileManager that is related to this ClassLoader
*/
private final EclipseCompilerClassFileManager classFileManager;
/**
* Classes that were loaded from libraries. They are no parsed for any
* annotations, but they are needed by JavaCompiler to perform valid
* compilation
*/
private Set<String> libraryClasses = CollectionFactory.newSet();
/**
* Creates new ScriptClassLoader with given ClassFileManger
*
* @param classFileManager
* classFileManager of this classLoader
*/
EclipseCompilerScriptClassLoader(EclipseCompilerClassFileManager classFileManager) {
super(new URL[] {});
this.classFileManager = classFileManager;
}
/**
* Creates new ScriptClassLoader with given ClassFileManger and another
* classLoader as parent
*
* @param classFileManager
* classFileManager of this classLoader
* @param parent
* parent classLoader
*/
EclipseCompilerScriptClassLoader(EclipseCompilerClassFileManager classFileManager, ClassLoader parent) {
super(new URL[] {}, parent);
this.classFileManager = classFileManager;
}
/**
* Returns ClassFileManager that is related to this ClassLoader
*
* @return classFileManager of this classLoader
*/
public EclipseCompilerClassFileManager getClassFileManager() {
return classFileManager;
}
/**
* AddsLibrary jar
*
* @param file
* jar file to add
* @throws IOException
* if any I/O error occur
*/
@Override
public void addLibrary(File file) throws IOException {
URL fileURL = file.toURI().toURL();
addURL(fileURL);
JarFile jarFile = new JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.endsWith(".class")) {
name = name.substring(0, name.length() - 6);
name = name.replace('/', '.');
libraryClasses.add(name);
}
}
jarFile.close();
}
/**
* Loads class from library, parent or compiled
*
* @param name
* class to load
* @return loaded class
* @throws ClassNotFoundException
* if class not found
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
BinaryClass bc = classFileManager.getCompiledClasses().get(name);
if (bc == null) {
return super.loadClass(name, true);
}
Class<?> c = bc.getDefinedClass();
if (c == null) {
byte[] b = bc.getBytes();
c = super.defineClass(name.replace('/', '.'), b, 0, b.length);
bc.setDefinedClass(c);
}
return c;
}
/**
* {@inheritDoc}
*/
@Override
public URL getResource(String name) {
if (!name.endsWith(".class")) {
return super.getResource(name);
} else {
String newName = name.substring(0, name.length() - 6);
newName = newName.replace('/', '.');
if (classFileManager.getCompiledClasses().containsKey(newName)) {
try {
return new URL(null,
EclipseCompilerVirtualClassURLStreamHandler.HANDLER_PROTOCOL
+ newName, urlStreamHandler);
} catch (MalformedURLException e) {
log.error("Can't create url for compiled class", e);
}
}
}
return super.getResource(name);
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getLibraryClasses() {
return Collections.unmodifiableSet(libraryClasses);
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getCompiledClasses() {
Set<String> compiledClasses = classFileManager.getCompiledClasses()
.keySet();
return Collections.unmodifiableSet(compiledClasses);
}
/**
* Returns list of classes that are members of a package
*
* @param packageName
* package to search for classes
* @return list of classes that are package members
* @throws IOException
* if was unable to load class
*/
public Set<JavaFileObject> getClassesForPackage(String packageName)
throws IOException {
Set<JavaFileObject> result = CollectionFactory.newSet();
// load parent
ClassLoader parent = getParent();
if (parent instanceof EclipseCompilerScriptClassLoader) {
EclipseCompilerScriptClassLoader pscl = (EclipseCompilerScriptClassLoader) parent;
result.addAll(pscl.getClassesForPackage(packageName));
}
// load current classloader compiled classes
for (String cn : classFileManager.getCompiledClasses().keySet()) {
if (ClassUtils.isPackageMember(cn, packageName)) {
BinaryClass bc = classFileManager.getCompiledClasses().get(cn);
result.add(bc);
}
}
// load libraries
for (String cn : libraryClasses) {
if (ClassUtils.isPackageMember(cn, packageName)) {
BinaryClass bc = new BinaryClass(cn);
try {
byte[] data = getRawClassByName(cn);
OutputStream os = bc.openOutputStream();
os.write(data);
} catch (IOException e) {
log.error("Error while loading class from package "
+ packageName, e);
throw e;
}
result.add(bc);
}
}
return result;
}
/**
* Finds class with the specified name from the URL search path. Any URLs
* referring to JAR files are loaded and opened as needed until the class is
* found.
*
* @param name
* the name of the class
* @return the resulting class data
* @throws IOException
* if the class could not be found
*/
protected byte[] getRawClassByName(String name) throws IOException {
URL resource = findResource(name.replace('.', '/').concat(".class"));
InputStream is = null;
byte[] clazz = null;
try {
is = resource.openStream();
clazz = IOUtils.toByteArray(is);
} catch (IOException e) {
log.error("Error while loading class data", e);
throw e;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
log.error("Error while closing stream", e);
}
}
}
return clazz;
}
}

View File

@@ -0,0 +1,75 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.ecj;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import com.l2jserver.service.game.scripting.impl.BinaryClass;
/**
* This class represents URL Connection that is used to "connect" to scripts
* binary data that was loaded by specified {@link EclipseScriptCompiler}.<br>
* <br>
* TODO: Implement all methods of {@link URLConnection} to ensure valid
* behaviour
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EclipseCompilerVirtualClassURLConnection extends URLConnection {
/**
* Input stream, is assigned from class
*/
private InputStream is;
/**
* Creates URL connections that "connects" to class binary data
*
* @param url
* class name
* @param cl
* classloader
*/
protected EclipseCompilerVirtualClassURLConnection(URL url, EclipseCompilerScriptClassLoader cl) {
super(url);
BinaryClass bc = cl.getClassFileManager().getCompiledClasses()
.get(url.getHost());
byte[] b = new byte[bc.getBytes().length];
System.arraycopy(bc.getBytes(), 0, b, 0, b.length);
is = new ByteArrayInputStream(b);
}
/**
* This method is ignored
*/
@Override
public void connect() throws IOException {
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getInputStream() throws IOException {
return is;
}
}

View File

@@ -0,0 +1,65 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.ecj;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/**
* This class represents URL Stream handler that accepts
* {@value #HANDLER_PROTOCOL} protocol
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EclipseCompilerVirtualClassURLStreamHandler extends URLStreamHandler {
/**
* Script Handler protocol for classes compiled from source
*/
public static final String HANDLER_PROTOCOL = "aescript://";
/**
* Script class loader that loaded those classes
*/
private final EclipseCompilerScriptClassLoader cl;
/**
* Creates new instance of url stream handler with given classloader
*
* @param cl
* ScriptClassLoaderImpl that was used to load compiled class
*/
public EclipseCompilerVirtualClassURLStreamHandler(EclipseCompilerScriptClassLoader cl) {
this.cl = cl;
}
/**
* Opens new URL connection for URL
*
* @param u
* url
* @return Opened connection
* @throws IOException
* never thrown
*/
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new EclipseCompilerVirtualClassURLConnection(u, cl);
}
}

View File

@@ -0,0 +1,255 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.ecj;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.service.game.scripting.CompilationResult;
import com.l2jserver.service.game.scripting.ScriptClassLoader;
import com.l2jserver.service.game.scripting.ScriptCompiler;
import com.l2jserver.service.game.scripting.impl.ErrorListener;
import com.l2jserver.service.game.scripting.impl.JavaSourceFromByteArray;
import com.l2jserver.service.game.scripting.impl.JavaSourceFromFile;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Wrapper for JavaCompiler api
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class EclipseScriptCompiler implements ScriptCompiler {
/**
* Logger for this class
*/
private static final Logger log = LoggerFactory
.getLogger(EclipseScriptCompiler.class);
/**
* Instance of JavaCompiler that will be used to compile classes
*/
protected final JavaCompiler javaCompiler;
/**
* List of jar files
*/
protected Iterable<File> libraries;
/**
* Parent classloader that has to be used for this compiler
*/
protected ScriptClassLoader parentClassLoader;
/**
* Creates new instance of JavaCompilerImpl. If system compiler is not
* available - throws RuntimeExcetion
*
* @throws RuntimeException
* if compiler is not available
*/
public EclipseScriptCompiler() {
this.javaCompiler = new EclipseCompiler();
if (javaCompiler == null) {
if (ToolProvider.getSystemJavaCompiler() != null) {
throw new RuntimeException(new InstantiationException(
"JavaCompiler is not aviable."));
}
}
}
/**
* Sets parent classLoader for this JavaCompilerImpl
*
* @param classLoader
* parent classloader
*/
@Override
public void setParentClassLoader(ScriptClassLoader classLoader) {
this.parentClassLoader = classLoader;
}
/**
* Sets jar files that should be used for this compiler as libraries
*
* @param files
* list of jar files
*/
@Override
public void setLibraries(Iterable<File> files) {
libraries = files;
}
/**
* Compiles given class.
*
* @param className
* Name of the class
* @param sourceCode
* source code
* @return CompilationResult with the class
* @throws RuntimeException
* if compilation failed with errros
*/
@Override
public CompilationResult compile(String className, byte[] sourceCode) {
return compile(new String[] { className }, new byte[][] { sourceCode });
}
/**
* Compiles list of classes. Amount of classNames must be equal to amount of
* sourceCodes
*
* @param classNames
* classNames
* @param sourceCode
* list of source codes
* @return CompilationResult with needed files
* @throws IllegalArgumentException
* if size of classNames not equals to size of sourceCodes
* @throws RuntimeException
* if compilation failed with errros
*/
@Override
public CompilationResult compile(String[] classNames, byte[][] sourceCode)
throws IllegalArgumentException {
if (classNames.length != sourceCode.length) {
throw new IllegalArgumentException(
"Amount of classes is not equal to amount of sources");
}
List<JavaFileObject> compilationUnits = CollectionFactory.newList();
for (int i = 0; i < classNames.length; i++) {
JavaFileObject compilationUnit = new JavaSourceFromByteArray(
classNames[i], sourceCode[i]);
compilationUnits.add(compilationUnit);
}
return doCompilation(compilationUnits);
}
/**
* Compiles given files. Files must be java sources.
*
* @param compilationUnits
* files to compile
* @return CompilationResult with classes
* @throws RuntimeException
* if compilation failed with errros
*/
@Override
public CompilationResult compile(Iterable<File> compilationUnits) {
List<JavaFileObject> list = CollectionFactory.newList();
for (File f : compilationUnits) {
list.add(new JavaSourceFromFile(f, JavaFileObject.Kind.SOURCE));
}
return doCompilation(list);
}
/**
* Actually performs compilation. Compiler expects sources in UTF-8
* encoding. Also compiler generates full debugging info for classes.
*
* @param compilationUnits
* Units that will be compiled
* @return CompilationResult with compiledClasses
* @throws RuntimeException
* if compilation failed with errros
*/
protected CompilationResult doCompilation(
Iterable<JavaFileObject> compilationUnits) {
List<String> options = Arrays.asList("-encoding", "UTF-8", "-g");
DiagnosticListener<JavaFileObject> listener = new ErrorListener();
EclipseCompilerClassFileManager manager = new EclipseCompilerClassFileManager(javaCompiler, listener);
manager.setParentClassLoader(parentClassLoader);
if (libraries != null) {
try {
manager.addLibraries(libraries);
} catch (IOException e) {
log.error("Can't set libraries for compiler.", e);
}
}
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, manager,
listener, options, null, compilationUnits);
if (!task.call()) {
throw new RuntimeException("Error while compiling classes");
}
ScriptClassLoader cl = manager.getClassLoader(null);
Class<?>[] compiledClasses = classNamesToClasses(manager
.getCompiledClasses().keySet(), cl);
return new CompilationResult(compiledClasses, cl);
}
/**
* Resolves list of classes by their names
*
* @param classNames
* names of the classes
* @param cl
* classLoader to use to resove classes
* @return resolved classes
* @throws RuntimeException
* if can't find class
*/
protected Class<?>[] classNamesToClasses(Collection<String> classNames,
ScriptClassLoader cl) {
Class<?>[] classes = new Class<?>[classNames.size()];
int i = 0;
for (String className : classNames) {
try {
Class<?> clazz = cl.loadClass(className);
classes[i] = clazz;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
i++;
}
return classes;
}
/**
* Only java files are supported by java compiler
*
* @return "java";
*/
@Override
public String[] getSupportedFileTypes() {
return new String[] { "java" };
}
}

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.javacc;
package com.l2jserver.service.game.scripting.impl.jdk;
import java.io.File;
import java.io.IOException;
@@ -44,7 +44,7 @@ import com.l2jserver.util.factory.CollectionFactory;
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ClassFileManager extends
public class JDKClassFileManager extends
ForwardingJavaFileManager<JavaFileManager> {
/**
@@ -56,7 +56,7 @@ public class ClassFileManager extends
/**
* Classloader that will be used to load compiled classes
*/
protected ScriptClassLoaderImpl loader;
protected JDKScriptClassLoader loader;
/**
* Parent classloader for loader
@@ -71,7 +71,7 @@ public class ClassFileManager extends
* @param listener
* class that will report compilation errors
*/
public ClassFileManager(JavaCompiler compiler,
public JDKClassFileManager(JavaCompiler compiler,
DiagnosticListener<? super JavaFileObject> listener) {
super(compiler.getStandardFileManager(listener, null, null));
}
@@ -108,19 +108,19 @@ public class ClassFileManager extends
* @return classLoader of this ClassFileManager
*/
@Override
public synchronized ScriptClassLoaderImpl getClassLoader(Location location) {
public synchronized JDKScriptClassLoader getClassLoader(Location location) {
if (loader == null) {
loader = AccessController
.doPrivileged(new PrivilegedAction<ScriptClassLoaderImpl>() {
.doPrivileged(new PrivilegedAction<JDKScriptClassLoader>() {
@Override
public ScriptClassLoaderImpl run() {
public JDKScriptClassLoader run() {
if (parentClassLoader != null) {
return new ScriptClassLoaderImpl(
ClassFileManager.this,
ClassFileManager.this.parentClassLoader);
return new JDKScriptClassLoader(
JDKClassFileManager.this,
JDKClassFileManager.this.parentClassLoader);
} else {
return new ScriptClassLoaderImpl(
ClassFileManager.this);
return new JDKScriptClassLoader(
JDKClassFileManager.this);
}
}
});
@@ -147,7 +147,7 @@ public class ClassFileManager extends
* if something goes wrong
*/
public void addLibrary(File file) throws IOException {
ScriptClassLoaderImpl classLoader = getClassLoader(null);
JDKScriptClassLoader classLoader = getClassLoader(null);
classLoader.addLibrary(file);
}

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.javacc;
package com.l2jserver.service.game.scripting.impl.jdk;
import java.io.File;
import java.io.IOException;
@@ -47,25 +47,25 @@ import com.l2jserver.util.factory.CollectionFactory;
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ScriptClassLoaderImpl extends ScriptClassLoader {
public class JDKScriptClassLoader extends ScriptClassLoader {
/**
* Logger
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptClassLoaderImpl.class);
.getLogger(JDKScriptClassLoader.class);
/**
* URL Stream handler to allow valid url generation by
* {@link #getResource(String)}
*/
private final VirtualClassURLStreamHandler urlStreamHandler = new VirtualClassURLStreamHandler(
private final JDKVirtualClassURLStreamHandler urlStreamHandler = new JDKVirtualClassURLStreamHandler(
this);
/**
* ClassFileManager that is related to this ClassLoader
*/
private final ClassFileManager classFileManager;
private final JDKClassFileManager classFileManager;
/**
* Classes that were loaded from libraries. They are no parsed for any
@@ -80,7 +80,7 @@ public class ScriptClassLoaderImpl extends ScriptClassLoader {
* @param classFileManager
* classFileManager of this classLoader
*/
ScriptClassLoaderImpl(ClassFileManager classFileManager) {
JDKScriptClassLoader(JDKClassFileManager classFileManager) {
super(new URL[] {});
this.classFileManager = classFileManager;
}
@@ -94,7 +94,7 @@ public class ScriptClassLoaderImpl extends ScriptClassLoader {
* @param parent
* parent classLoader
*/
ScriptClassLoaderImpl(ClassFileManager classFileManager, ClassLoader parent) {
JDKScriptClassLoader(JDKClassFileManager classFileManager, ClassLoader parent) {
super(new URL[] {}, parent);
this.classFileManager = classFileManager;
}
@@ -104,7 +104,7 @@ public class ScriptClassLoaderImpl extends ScriptClassLoader {
*
* @return classFileManager of this classLoader
*/
public ClassFileManager getClassFileManager() {
public JDKClassFileManager getClassFileManager() {
return classFileManager;
}
@@ -177,7 +177,7 @@ public class ScriptClassLoaderImpl extends ScriptClassLoader {
if (classFileManager.getCompiledClasses().containsKey(newName)) {
try {
return new URL(null,
VirtualClassURLStreamHandler.HANDLER_PROTOCOL
JDKVirtualClassURLStreamHandler.HANDLER_PROTOCOL
+ newName, urlStreamHandler);
} catch (MalformedURLException e) {
log.error("Can't create url for compiled class", e);
@@ -221,8 +221,8 @@ public class ScriptClassLoaderImpl extends ScriptClassLoader {
// load parent
ClassLoader parent = getParent();
if (parent instanceof ScriptClassLoaderImpl) {
ScriptClassLoaderImpl pscl = (ScriptClassLoaderImpl) parent;
if (parent instanceof JDKScriptClassLoader) {
JDKScriptClassLoader pscl = (JDKScriptClassLoader) parent;
result.addAll(pscl.getClassesForPackage(packageName));
}

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.javacc;
package com.l2jserver.service.game.scripting.impl.jdk;
import java.io.File;
import java.io.IOException;
@@ -43,12 +43,12 @@ import com.l2jserver.util.factory.CollectionFactory;
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ScriptCompilerImpl implements ScriptCompiler {
public class JDKScriptCompiler implements ScriptCompiler {
/**
* Logger for this class
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptCompilerImpl.class);
.getLogger(JDKScriptCompiler.class);
/**
* Instance of JavaCompiler that will be used to compile classes
@@ -72,7 +72,7 @@ public class ScriptCompilerImpl implements ScriptCompiler {
* @throws RuntimeException
* if compiler is not available
*/
public ScriptCompilerImpl() {
public JDKScriptCompiler() {
this.javaCompiler = ToolProvider.getSystemJavaCompiler();
if (javaCompiler == null) {
@@ -189,7 +189,7 @@ public class ScriptCompilerImpl implements ScriptCompiler {
Iterable<JavaFileObject> compilationUnits) {
List<String> options = Arrays.asList("-encoding", "UTF-8", "-g");
DiagnosticListener<JavaFileObject> listener = new ErrorListener();
ClassFileManager manager = new ClassFileManager(javaCompiler, listener);
JDKClassFileManager manager = new JDKClassFileManager(javaCompiler, listener);
manager.setParentClassLoader(parentClassLoader);
if (libraries != null) {

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.javacc;
package com.l2jserver.service.game.scripting.impl.jdk;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -26,14 +26,14 @@ import com.l2jserver.service.game.scripting.impl.BinaryClass;
/**
* This class represents URL Connection that is used to "connect" to scripts
* binary data that was loaded by specified {@link ScriptCompilerImpl}.<br>
* binary data that was loaded by specified {@link JDKScriptCompiler}.<br>
* <br>
* TODO: Implement all methods of {@link URLConnection} to ensure valid
* behaviour
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class VirtualClassURLConnection extends URLConnection {
public class JDKVirtualClassURLConnection extends URLConnection {
/**
* Input stream, is assigned from class
@@ -48,7 +48,7 @@ public class VirtualClassURLConnection extends URLConnection {
* @param cl
* classloader
*/
protected VirtualClassURLConnection(URL url, ScriptClassLoaderImpl cl) {
protected JDKVirtualClassURLConnection(URL url, JDKScriptClassLoader cl) {
super(url);
BinaryClass bc = cl.getClassFileManager().getCompiledClasses()
.get(url.getHost());

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.javacc;
package com.l2jserver.service.game.scripting.impl.jdk;
import java.io.IOException;
import java.net.URL;
@@ -27,7 +27,7 @@ import java.net.URLStreamHandler;
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class VirtualClassURLStreamHandler extends URLStreamHandler {
public class JDKVirtualClassURLStreamHandler extends URLStreamHandler {
/**
* Script Handler protocol for classes compiled from source
@@ -37,7 +37,7 @@ public class VirtualClassURLStreamHandler extends URLStreamHandler {
/**
* Script class loader that loaded those classes
*/
private final ScriptClassLoaderImpl cl;
private final JDKScriptClassLoader cl;
/**
* Creates new instance of url stream handler with given classloader
@@ -45,7 +45,7 @@ public class VirtualClassURLStreamHandler extends URLStreamHandler {
* @param cl
* ScriptClassLoaderImpl that was used to load compiled class
*/
public VirtualClassURLStreamHandler(ScriptClassLoaderImpl cl) {
public JDKVirtualClassURLStreamHandler(JDKScriptClassLoader cl) {
this.cl = cl;
}
@@ -60,6 +60,6 @@ public class VirtualClassURLStreamHandler extends URLStreamHandler {
*/
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new VirtualClassURLConnection(u, cl);
return new JDKVirtualClassURLConnection(u, cl);
}
}

View File

@@ -25,7 +25,7 @@ import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import com.l2jserver.service.game.scripting.impl.javacc.ScriptCompilerImpl;
import com.l2jserver.service.game.scripting.impl.ecj.EclipseScriptCompiler;
/**
* Simple class that represents script info.<br>
@@ -60,7 +60,7 @@ public class ScriptInfo {
* Default compiler class name.
*/
@XmlElement(name = "compiler")
private String compilerClass = ScriptCompilerImpl.class.getName();
private String compilerClass = EclipseScriptCompiler.class.getName();
/**
* Returns root of script context

View File

@@ -46,7 +46,6 @@ public class KnownListUpdateFilter extends AndFilter<PositionableObject> {
* @param old
* the old position
*/
@SuppressWarnings("unchecked")
public KnownListUpdateFilter(PositionableObject object, Point3D old) {
super(new KnownListFilter(object), new NotFilter<PositionableObject>(
new RangePointFilter(old, KNOWNLIST_RANGE)));

View File

@@ -55,7 +55,8 @@ public class ScriptingServiceImplTest {
final ScriptContext context = contexts.get(0);
Class<?> clazz = context.getClassLoader().loadClass(
"test.ScriptingCompilerTest");
Assert.assertNotNull(clazz.newInstance());
Assert.assertNotNull(clazz);
Assert.assertEquals("ScriptingCompilerTest", clazz.getSimpleName());
Assert.assertEquals("test.ScriptingCompilerTest", clazz.getName());
}
}

View File

@@ -1,43 +0,0 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.scripting.impl.compiled;
import java.io.File;
import org.apache.commons.io.FileUtils;
import com.l2jserver.service.game.scripting.CompilationResult;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class PrecompiledScriptCompilerTest {
/**
* Test method for
* {@link com.l2jserver.service.game.scripting.impl.compiled.PrecompiledScriptCompiler#compile(java.lang.Iterable)}
* .
*/
// @Test
public void testCompileIterableOfFile() {
final PrecompiledScriptCompiler compiler = new PrecompiledScriptCompiler();
final CompilationResult result = compiler.compile(FileUtils.listFiles(
new File("target/scripts/script/template"),
new String[] { "class" }, true));
System.out.println(result.getCompiledClasses()[0]);
}
}

View File

@@ -18,6 +18,6 @@
-->
<scriptlist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../contexts.xsd">
xsi:noNamespaceSchemaLocation="../../../../data/contexts.xsd">
<scriptinfo root="./src/test/resources/scripting"/>
</scriptlist>