1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-05 23:22:47 +00:00

Scripting engine

Change-Id: I5d6df6fd0d76cc07fd2631b5589f5200d9e50000
This commit is contained in:
rogiel
2011-04-30 09:31:32 -03:00
parent 8984654ed5
commit d76e80f9a0
55 changed files with 2953 additions and 146 deletions

View File

@@ -4,12 +4,11 @@
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry excluding="mysql5/|db4o/" kind="src" path="src/dao"/>
<classpathentry kind="src" path="src/dao/mysql5"/>
<classpathentry kind="src" path="src/dao/db4o"/>
<classpathentry kind="src" path="data/scripts"/>
<classpathentry kind="src" path="data/script"/>
<classpathentry kind="src" path="data/template"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

36
data/contexts.xsd Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ This file is part of l2jserver.
~
~ 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/>.
-->
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="scriptinfo" type="scriptInfo"/>
<xs:element name="scriptlist" type="scriptList"/>
<xs:complexType name="scriptList">
<xs:sequence>
<xs:element ref="scriptinfo" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="scriptInfo">
<xs:sequence>
<xs:element ref="scriptinfo" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="library" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="root" type="xs:string" use="required"/>
</xs:complexType>
</xs:schema>

View File

@@ -1,7 +1,7 @@
package com.l2jserver.script;
package script;
import com.l2jserver.model.world.capability.Scriptable;
import com.l2jserver.service.game.script.Script;
import com.l2jserver.service.game.scripting.Script;
public class DummyScript implements Script<Scriptable> {
@Override

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.armor;
package script.template.armor;
import com.l2jserver.model.id.TemplateID;
import com.l2jserver.model.template.ArmorTemplate;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.armor;
package script.template.armor;
import com.l2jserver.model.id.TemplateID;
import com.l2jserver.model.template.ArmorTemplate;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.armor;
package script.template.armor;
import com.l2jserver.model.world.capability.Attackable;
import com.l2jserver.model.world.capability.Attacker;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.armor;
package script.template.armor;
import com.l2jserver.model.world.capability.Attackable;
import com.l2jserver.model.world.capability.Attacker;

View File

@@ -0,0 +1,12 @@
package script.template.item;
import com.l2jserver.model.id.TemplateID;
import com.l2jserver.model.template.ItemTemplate;
public class AdenaItemTemplate extends ItemTemplate {
public static final TemplateID ID = new TemplateID(57);
public AdenaItemTemplate() {
super(ID);
}
}

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.item;
package script.template.item;
import com.l2jserver.model.template.PotionTemplate;
import com.l2jserver.model.world.capability.Attackable;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.skill;
package script.template.skill;
import com.l2jserver.model.template.SkillTemplate;
import com.l2jserver.model.world.capability.Castable;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.model.template.weapon;
package script.template.weapon;
import com.l2jserver.model.template.WeaponTemplate;
import com.l2jserver.model.world.capability.Attackable;

View File

@@ -1,15 +0,0 @@
package com.l2jserver.db.dao.db4o;
import com.google.inject.Inject;
import com.l2jserver.service.database.AbstractDAO;
import com.l2jserver.service.database.DB4ODatabaseService;
public class AbstractDB4ODAO<T> extends AbstractDAO<T> implements BD4ODAO {
protected final DB4ODatabaseService database;
@Inject
protected AbstractDB4ODAO(DB4ODatabaseService database) {
super(database);
this.database = database;
}
}

View File

@@ -1,5 +0,0 @@
package com.l2jserver.db.dao.db4o;
public interface BD4ODAO {
}

View File

@@ -1,34 +0,0 @@
package com.l2jserver.db.dao.db4o;
import java.util.List;
import com.google.inject.Inject;
import com.l2jserver.db.dao.CharacterDAO;
import com.l2jserver.model.id.CharacterID;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.service.database.DB4ODatabaseService;
public class DB4OCharacterDAO extends AbstractDB4ODAO<L2Character> implements CharacterDAO {
@Inject
protected DB4OCharacterDAO(DB4ODatabaseService database) {
super(database);
}
@Override
public L2Character load(CharacterID id) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean save(L2Character character) {
// TODO Auto-generated method stub
return false;
}
@Override
public List<CharacterID> listIDs() {
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -1,4 +1,4 @@
package com.l2jserver.db.dao.mysql5;
package script.dao.mysql5;
import com.google.inject.Inject;
import com.l2jserver.service.database.AbstractDAO;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.db.dao.mysql5;
package script.dao.mysql5;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.db.dao.mysql5;
package script.dao.mysql5;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

View File

@@ -1,4 +1,4 @@
package com.l2jserver.db.dao.mysql5;
package script.dao.mysql5;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

View File

@@ -1,7 +1,8 @@
package com.l2jserver;
import script.dao.mysql5.DAOModuleMySQL5;
import com.google.inject.AbstractModule;
import com.l2jserver.db.dao.mysql5.DAOModuleMySQL5;
import com.l2jserver.model.id.factory.IDFactoryModule;
import com.l2jserver.routines.GameServerInitializationRoutine;
import com.l2jserver.service.BasicServiceModule;

View File

@@ -9,14 +9,13 @@ import org.jboss.netty.logging.InternalLogLevel;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.l2jserver.game.net.codec.Lineage2FrameDecoder;
import com.l2jserver.game.net.codec.Lineage2Decrypter;
import com.l2jserver.game.net.codec.Lineage2FrameEncoder;
import com.l2jserver.game.net.codec.Lineage2Encrypter;
import com.l2jserver.game.net.codec.Lineage2FrameDecoder;
import com.l2jserver.game.net.codec.Lineage2FrameEncoder;
import com.l2jserver.game.net.codec.Lineage2PacketReader;
import com.l2jserver.game.net.codec.Lineage2PacketWriter;
import com.l2jserver.game.net.handler.Lineage2PacketHandler;
import com.l2jserver.service.logging.LoggingService;
public class Lineage2PipelineFactory implements ChannelPipelineFactory {
private final Injector injector;
@@ -32,15 +31,14 @@ public class Lineage2PipelineFactory implements ChannelPipelineFactory {
pipeline.addLast("frame.encoder", new Lineage2FrameEncoder());
pipeline.addLast("frame.decoder", new Lineage2FrameDecoder());
pipeline.addLast(Lineage2Encrypter.HANDLER_NAME,
new Lineage2Encrypter());
pipeline.addLast(Lineage2Decrypter.HANDLER_NAME,
new Lineage2Decrypter());
pipeline.addLast("packet.writer", new Lineage2PacketWriter());
pipeline.addLast("packet.reader", new Lineage2PacketReader(injector,
injector.getInstance(LoggingService.class)));
pipeline.addLast("packet.reader", new Lineage2PacketReader(injector));
pipeline.addLast("packet.handler", new Lineage2PacketHandler());

View File

@@ -4,23 +4,23 @@ import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.l2jserver.game.net.packet.ClientPacket;
import com.l2jserver.game.net.packet.client.AuthLoginPacket;
import com.l2jserver.game.net.packet.client.ProtocolVersionPacket;
import com.l2jserver.service.logging.Logger;
import com.l2jserver.service.logging.LoggingService;
public class Lineage2PacketReader extends OneToOneDecoder {
private final Injector injector;
private final Logger logger;
private final Logger logger = LoggerFactory
.getLogger(Lineage2PacketReader.class);
@Inject
public Lineage2PacketReader(Injector injector, LoggingService logging) {
public Lineage2PacketReader(Injector injector) {
this.injector = injector;
this.logger = logging.getLogger(Lineage2PacketReader.class);
}
@Override

View File

@@ -3,20 +3,20 @@ package com.l2jserver.game.net.packet.client;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.L2JConstants;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.AbstractClientPacket;
import com.l2jserver.game.net.packet.server.KeyPacket;
import com.l2jserver.service.logging.Logger;
import com.l2jserver.service.logging.guice.InjectLogger;
public class ProtocolVersionPacket extends AbstractClientPacket {
public static final int OPCODE = 0x0e;
// services
@InjectLogger
private final Logger logger = null;
private final Logger logger = LoggerFactory
.getLogger(ProtocolVersionPacket.class);
// packet
private long version;

View File

@@ -1,7 +1,7 @@
package com.l2jserver.model.world.capability;
import com.l2jserver.model.world.AbstractObject;
import com.l2jserver.service.game.script.Script;
import com.l2jserver.service.game.scripting.Script;
/**
* Defines an {@link AbstractObject} that can be controller by an {@link Script}

View File

@@ -1,8 +0,0 @@
package com.l2jserver.service.compiler;
import com.l2jserver.service.Service;
public interface CompilerService extends Service {
Class<?> compile(byte[] clazz);
ClassLoader getClassLoader();
}

View File

@@ -1,11 +0,0 @@
package com.l2jserver.service.compiler;
public class DynamicClassLoader extends ClassLoader {
public DynamicClassLoader() {
super();
}
public DynamicClassLoader(ClassLoader parent) {
super(parent);
}
}

View File

@@ -1,29 +0,0 @@
package com.l2jserver.service.compiler;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
public class JavacCompilerService implements CompilerService {
@Override
public void start() throws ServiceStartException {
// TODO Auto-generated method stub
}
@Override
public Class<?> compile(byte[] clazz) {
// TODO Auto-generated method stub
return null;
}
@Override
public ClassLoader getClassLoader() {
// TODO Auto-generated method stub
return null;
}
@Override
public void stop() throws ServiceStopException {
}
}

View File

@@ -1,7 +0,0 @@
package com.l2jserver.service.game.script;
import com.l2jserver.service.Service;
public interface ScriptingService extends Service {
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
/**
* This class represents compilation result of script context
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class CompilationResult {
/**
* List of classes that were compiled by compiler
*/
private final Class<?>[] compiledClasses;
/**
* Classloader that was used to load classes
*/
private final ScriptClassLoader classLoader;
/**
* Creates new instance of CompilationResult with classes that has to be
* parsed and classloader that was used to load classes
*
* @param compiledClasses
* classes compiled by compiler
* @param classLoader
* classloader that was used by compiler
*/
public CompilationResult(Class<?>[] compiledClasses,
ScriptClassLoader classLoader) {
this.compiledClasses = compiledClasses;
this.classLoader = classLoader;
}
/**
* Returns classLoader that was used by compiler
*
* @return classloader that was used by compiler
*/
public ScriptClassLoader getClassLoader() {
return classLoader;
}
/**
* Retunrs list of classes that were compiled
*
* @return list of classes that were compiled
*/
public Class<?>[] getCompiledClasses() {
return compiledClasses;
}
}

View File

@@ -1,4 +1,4 @@
package com.l2jserver.service.game.script;
package com.l2jserver.service.game.scripting;
import com.l2jserver.model.world.capability.Scriptable;
@@ -11,6 +11,6 @@ public interface Script<O extends Scriptable> extends Runnable {
void load(O object);
void unload();
O getObject();
}

View File

@@ -0,0 +1,96 @@
/*
* 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;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.util.Set;
/**
*
* Abstract class loader that should be extended by child classloaders. If
* needed, this class should wrap another classloader.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class ScriptClassLoader extends URLClassLoader {
/**
* Just for compatibility with {@link URLClassLoader}
*
* @param urls
* list of urls
* @param parent
* parent classloader
*/
public ScriptClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
/**
* Just for compatibility with {@link URLClassLoader}
*
* @param urls
* list of urls
*/
public ScriptClassLoader(URL[] urls) {
super(urls);
}
/**
* Just for compatibility with {@link URLClassLoader}
*
* @param urls
* list of urls
* @param parent
* parent classloader
* @param factory
* {@link java.net.URLStreamHandlerFactory}
*/
public ScriptClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(urls, parent, factory);
}
/**
* Adds library to this classloader, it shuould be jar file
*
* @param file
* jar file
* @throws IOException
* if can't add library
*/
public abstract void addLibrary(File file) throws IOException;
/**
* Returns unmodifiable set of class names that were loaded from libraries
*
* @return unmodifiable set of class names that were loaded from libraries
*/
public abstract Set<String> getLibraryClasses();
/**
* Retuns unmodifiable set of class names that were compiled
*
* @return unmodifiable set of class names that were compiled
*/
public abstract Set<String> getCompiledClasses();
}

View File

@@ -0,0 +1,90 @@
/*
* 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;
import java.io.File;
/**
* This interface reperesents common functionality list that should be available
* for any commpiler that is going to be used with scripting engine. For
* instance, groovy can be used, hoever it produces by far not the best bytecode
* so by default javac from sun is used.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ScriptCompiler {
/**
* Sets parent class loader for this compiler.<br>
* <br>
* <font color="red">Warning, for now only</font>
*
* @param classLoader
* ScriptClassLoader that will be used as parent
*/
void setParentClassLoader(ScriptClassLoader classLoader);
/**
* List of jar files that are required for compilation
*
* @param files
* list of jar files
*/
void setLibraires(Iterable<File> files);
/**
* Compiles single class that is represented as string
*
* @param className
* class name
* @param sourceCode
* class sourse code
* @return {@link CompilationResult}
*/
CompilationResult compile(String className, String sourceCode);
/**
* Compiles classes that are represented as strings
*
* @param className
* class names
* @param sourceCode
* class sources
* @return {@link CompilationResult}
* @throws IllegalArgumentException
* if number of class names != number of sources
*/
CompilationResult compile(String[] className, String[] sourceCode)
throws IllegalArgumentException;
/**
* Compiles list of files
*
* @param compilationUnits
* list of files
* @return {@link CompilationResult}
*/
CompilationResult compile(Iterable<File> compilationUnits);
/**
* Returns array of supported file types. This files will be threated as
* source files.
*
* @return array of supported file types.
*/
String[] getSupportedFileTypes();
}

View File

@@ -0,0 +1,181 @@
/*
* 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;
import java.io.File;
import java.util.Collection;
import com.l2jserver.service.game.scripting.classlistener.ClassListener;
import com.l2jserver.service.game.scripting.classlistener.DefaultClassListener;
import com.l2jserver.service.game.scripting.metadata.OnClassLoad;
import com.l2jserver.service.game.scripting.metadata.OnClassUnload;
/**
* This class represents script context that can be loaded, unloaded, etc...<br>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ScriptContext {
/**
* Initializes script context. Calls the compilation task.<br>
* After compilation static methods marked with {@link OnClassLoad} are
* invoked
*/
void init();
/**
* Notifies all script classes that they must save their data and release
* resources to prevent memory leaks. It's done via static methods with
* {@link OnClassUnload} annotation
*/
void shutdown();
/**
* Invokes {@link #shutdown()}, after that invokes {@link #init()}. Root
* folder remains the same, but new compiler and classloader are used.
*/
void reload();
/**
* Returns the root directory for script engine. Only one script engine per
* root directory is allowed.
*
* @return root directory for script engine
*/
File getRoot();
/**
* Returns compilation result of this script context
*
* @return compilation result
*/
CompilationResult getCompilationResult();
/**
* Returns true if this script context is loaded
*
* @return true if context is initialized
*/
boolean isInitialized();
/**
* Sets files that represents jar files, they will be used as libraries
*
* @param files
* that points to jar file, will be used as libraries
*/
void setLibraries(Iterable<File> files);
/**
* Returns list of files that are used as libraries for this script context
*
* @return list of libraries
*/
Iterable<File> getLibraries();
/**
* Returns parent script context of this context. Returns null if none.
*
* @return parent Script context of this context or null
*/
ScriptContext getParentScriptContext();
/**
* Returns list of child contexts or null if no contextes present
*
* @return list of child contexts or null
*/
Collection<ScriptContext> getChildScriptContexts();
/**
* Adds child contexts to this context. If this context is initialized -
* chiled context will be initialized immideatly. In other case child
* context will be just added and initialized when {@link #init()} would be
* called. Duplicated child contexts are not allowed, in such case child
* will be ignored
*
* @param context
* child context
*/
void addChildScriptContext(ScriptContext context);
/**
* Sets the class listener for this script context.
*
* @param cl
* class listener
*/
void setClassListener(ClassListener cl);
/**
* Returns class listener associated with this ScriptContext.<br>
* If it's null - returns parent classListener.<br>
* If parent is null and classListener is null - it will set
* {@link DefaultClassListener} as class listener and return it
*
* @return Associated class listener
*/
ClassListener getClassListener();
/**
* Sets compiler class name for this script context.<br>
* Compiler is not inherrited by children.<br>
*
* @param className
* compiler class name
*/
void setCompilerClassName(String className);
/**
* Returns compiler class name that will be used for this script context.
*
* @return compiler class name that will be used for tis script context
*/
String getCompilerClassName();
/**
* Tests if this ScriptContext is equal to another ScriptContext.
* Comparation is done by comparing root files and parent contexts (if there
* is any parent)
*
* @param obj
* object to compare with
* @return result of comparation
*/
@Override
boolean equals(Object obj);
/**
* Returns hashCoded of this ScriptContext. Hashcode is calculated using
* root file and parent context(if available)
*
* @return hashcode
*/
@Override
int hashCode();
/**
* This method overrides finalization to ensure that active script context
* will not be collected by GC. If such situation happens -
* {@link #shutdown()} is called to ensure that resources were released.
*
* @throws Throwable
* if something goes wrong during finalization
*/
void finalize() throws Throwable;
}

View File

@@ -0,0 +1,42 @@
/*
* 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;
import java.io.File;
import com.l2jserver.service.game.scripting.impl.ScriptContextImpl;
/**
* This class is script context provider. We can switch to any other
* ScriptContext implementation later, so it's good to have factory class
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public final class ScriptContextFactory {
public static ScriptContext getScriptContext(File root, ScriptContext parent)
throws InstantiationException {
ScriptContextImpl ctx;
if (parent == null) {
ctx = new ScriptContextImpl(root);
} else {
ctx = new ScriptContextImpl(root, parent);
parent.addChildScriptContext(ctx);
}
return ctx;
}
}

View File

@@ -0,0 +1,22 @@
package com.l2jserver.service.game.scripting;
import java.io.File;
import com.l2jserver.service.Service;
public interface ScriptingService extends Service {
/**
* Creates script context, sets the root context. Adds child context if
* needed
*
* @param root
* file that will be threated as root for compiler
* @param parent
* parent of new ScriptContext
* @return ScriptContext with presetted root file
* @throws InstantiationException
* if java compiler is not aviable
*/
ScriptContext getScriptContext(File root, ScriptContext parent)
throws InstantiationException;
}

View File

@@ -0,0 +1,32 @@
package com.l2jserver.service.game.scripting;
import java.io.File;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.game.scripting.impl.ScriptContextImpl;
public class ScriptingServiceImpl implements ScriptingService {
@Override
public void start() throws ServiceStartException {
}
@Override
public ScriptContext getScriptContext(File root, ScriptContext parent)
throws InstantiationException {
ScriptContextImpl ctx;
if (parent == null) {
ctx = new ScriptContextImpl(root);
} else {
ctx = new ScriptContextImpl(root, parent);
parent.addChildScriptContext(ctx);
}
return ctx;
}
@Override
public void stop() throws ServiceStopException {
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.classlistener;
/**
* This interface implements listener that is called post class load/before
* class unload.<br>
* Default implementation is: {@link DefaultClassListener}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface ClassListener {
/**
* This method is invoked after classes were loaded. As areguments are
* passes all loaded classes
*
* @param classes
* classes that were loaded
*/
public void postLoad(Class<?>... classes);
/**
* This method is invoked before class unloading. As argument are passes all
* loaded classes
*
* @param classes
* classes that were loaded
*/
public void preUnload(Class<?>... classes);
}

View File

@@ -0,0 +1,88 @@
/*
* 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.classlistener;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.service.game.scripting.metadata.OnClassLoad;
import com.l2jserver.service.game.scripting.metadata.OnClassUnload;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class DefaultClassListener implements ClassListener {
private static final Logger log = LoggerFactory
.getLogger(DefaultClassListener.class);
@Override
public void postLoad(Class<?>... classes) {
for (Class<?> c : classes) {
doMethodInvoke(c.getDeclaredMethods(), OnClassLoad.class);
}
}
@Override
public void preUnload(Class<?>... classes) {
for (Class<?> c : classes) {
doMethodInvoke(c.getDeclaredMethods(), OnClassUnload.class);
}
}
/**
* Actually invokes method where given annotation class is present. Only
* static methods can be invoked
*
* @param methods
* Methods to scan for annotations
* @param annotationClass
* class of annotation to search for
*/
static void doMethodInvoke(Method[] methods,
Class<? extends Annotation> annotationClass) {
for (Method m : methods) {
if (!Modifier.isStatic(m.getModifiers())) {
continue;
}
boolean accessible = m.isAccessible();
m.setAccessible(true);
if (m.getAnnotation(annotationClass) != null) {
try {
m.invoke(null);
} catch (IllegalAccessException e) {
log.error("Can't access method " + m.getName()
+ " of class " + m.getDeclaringClass().getName(), e);
} catch (InvocationTargetException e) {
log.error("Can't invoke method " + m.getName()
+ " of class " + m.getDeclaringClass().getName(), e);
}
}
m.setAccessible(accessible);
}
}
}

View File

@@ -0,0 +1,337 @@
/*
* 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;
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.service.game.scripting.CompilationResult;
import com.l2jserver.service.game.scripting.ScriptCompiler;
import com.l2jserver.service.game.scripting.ScriptContext;
import com.l2jserver.service.game.scripting.classlistener.ClassListener;
import com.l2jserver.service.game.scripting.classlistener.DefaultClassListener;
/**
* This class is actual implementation of {@link ScriptContext}
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ScriptContextImpl implements ScriptContext {
/**
* logger for this class
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptContextImpl.class);
/**
* Script context that is parent for this script context
*/
private final ScriptContext parentScriptContext;
/**
* Libraries (list of jar files) that have to be loaded class loader
*/
private Iterable<File> libraries;
/**
* Root directory of this script context. It and it's subdirectories will be
* scanned for .java files.
*/
private final File root;
/**
* Result of compilation of script context
*/
private CompilationResult compilationResult;
/**
* List of child script contexts
*/
private Set<ScriptContext> childScriptContexts;
/**
* Classlistener for this script context
*/
private ClassListener classListener;
/**
* Class name of the compiler that will be used to compile sources
*/
private String compilerClassName;
/**
* Creates new scriptcontext with given root file
*
* @param root
* file that represents root directory of this script context
* @throws NullPointerException
* if root is null
* @throws IllegalArgumentException
* if root directory doesn't exists or is not a directory
*/
public ScriptContextImpl(File root) {
this(root, null);
}
/**
* Creates new ScriptContext with given file as root and another
* ScriptContext as parent
*
* @param root
* file that represents root directory of this script context
* @param parent
* parent ScriptContex. It's classes and libraries will be
* accessible for this script context
* @throws NullPointerException
* if root is null
* @throws IllegalArgumentException
* if root directory doesn't exists or is not a directory
*/
public ScriptContextImpl(File root, ScriptContext parent) {
if (root == null) {
throw new NullPointerException("Root file must be specified");
}
if (!root.exists() || !root.isDirectory()) {
throw new IllegalArgumentException(
"Root directory not exists or is not a directory");
}
this.root = root;
this.parentScriptContext = parent;
}
@Override
public synchronized void init() {
if (compilationResult != null) {
log.error("", new Exception(
"Init request on initialized ScriptContext"));
return;
}
ScriptCompiler scriptCompiler = instantiateCompiler();
@SuppressWarnings("unchecked")
Collection<File> files = FileUtils.listFiles(root,
scriptCompiler.getSupportedFileTypes(), true);
if (parentScriptContext != null) {
scriptCompiler.setParentClassLoader(parentScriptContext
.getCompilationResult().getClassLoader());
}
scriptCompiler.setLibraires(libraries);
compilationResult = scriptCompiler.compile(files);
getClassListener().postLoad(compilationResult.getCompiledClasses());
if (childScriptContexts != null) {
for (ScriptContext context : childScriptContexts) {
context.init();
}
}
}
@Override
public synchronized void shutdown() {
if (compilationResult == null) {
log.error("Shutdown of not initialized stript context",
new Exception());
return;
}
if (childScriptContexts != null) {
for (ScriptContext child : childScriptContexts) {
child.shutdown();
}
}
getClassListener().preUnload(compilationResult.getCompiledClasses());
compilationResult = null;
}
@Override
public void reload() {
shutdown();
init();
}
@Override
public File getRoot() {
return root;
}
@Override
public CompilationResult getCompilationResult() {
return compilationResult;
}
@Override
public synchronized boolean isInitialized() {
return compilationResult != null;
}
@Override
public void setLibraries(Iterable<File> files) {
this.libraries = files;
}
@Override
public Iterable<File> getLibraries() {
return libraries;
}
@Override
public ScriptContext getParentScriptContext() {
return parentScriptContext;
}
@Override
public Collection<ScriptContext> getChildScriptContexts() {
return childScriptContexts;
}
/**
* {@inheritDoc}
*/
@Override
public void addChildScriptContext(ScriptContext context) {
synchronized (this) {
if (childScriptContexts == null) {
childScriptContexts = new HashSet<ScriptContext>();
}
if (childScriptContexts.contains(context)) {
log.error("Double child definition, root: "
+ root.getAbsolutePath() + ", child: "
+ context.getRoot().getAbsolutePath());
return;
}
if (isInitialized()) {
context.init();
}
}
childScriptContexts.add(context);
}
@Override
public void setClassListener(ClassListener cl) {
classListener = cl;
}
@Override
public ClassListener getClassListener() {
if (classListener == null) {
if (getParentScriptContext() == null) {
setClassListener(new DefaultClassListener());
return classListener;
} else {
return getParentScriptContext().getClassListener();
}
} else {
return classListener;
}
}
@Override
public void setCompilerClassName(String className) {
this.compilerClassName = className;
}
@Override
public String getCompilerClassName() {
return this.compilerClassName;
}
/**
* Creates new instance of ScriptCompiler that should be used with this
* ScriptContext
*
* @return instance of ScriptCompiler
* @throws RuntimeException
* if failed to create instance
*/
protected ScriptCompiler instantiateCompiler() throws RuntimeException {
ClassLoader cl = getClass().getClassLoader();
if (getParentScriptContext() != null) {
cl = getParentScriptContext().getCompilationResult()
.getClassLoader();
}
ScriptCompiler sc;
try {
sc = (ScriptCompiler) Class.forName(getCompilerClassName(), true,
cl).newInstance();
} catch (Exception e) {
RuntimeException e1 = new RuntimeException(
"Can't create instance of compiler", e);
log.error("Compiler exception", e1);
throw e1;
}
return sc;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ScriptContextImpl)) {
return false;
}
ScriptContextImpl another = (ScriptContextImpl) obj;
if (parentScriptContext == null) {
return another.getRoot().equals(root);
} else {
return another.getRoot().equals(root)
&& parentScriptContext.equals(another.parentScriptContext);
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int result = parentScriptContext != null ? parentScriptContext
.hashCode() : 0;
result = 31 * result + root.hashCode();
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void finalize() throws Throwable {
if (compilationResult != null) {
log.error("Finalization of initialized ScriptContext. Forcing context shutdown.");
shutdown();
}
super.finalize();
}
}

View File

@@ -0,0 +1,212 @@
/*
* 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.javacc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaFileObject;
/**
* This class is just a hack to make javac compiler work with classes loaded by
* prevoius classloader. Also it's used as container for loaded class
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class BinaryClass implements JavaFileObject {
/**
* ClassName
*/
private final String name;
/**
* Class data will be written here
*/
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
/**
* Locaded class will be set here
*/
private Class<?> definedClass;
/**
* Constructor that accepts class name as parameter
*
* @param name
* class name
*/
protected BinaryClass(String name) {
this.name = name;
}
/**
* Throws {@link UnsupportedOperationException}
*
* @return nothing
*/
@Override
public URI toUri() {
throw new UnsupportedOperationException();
}
/**
* Returns name of this class with ".class" suffix
*
* @return name of this class with ".class" suffix
*/
@Override
public String getName() {
return name + ".class";
}
/**
* Creates new ByteArrayInputStream, it just wraps class binary data
*
* @return input stream for class data
* @throws IOException
* never thrown
*/
@Override
public InputStream openInputStream() throws IOException {
return new ByteArrayInputStream(baos.toByteArray());
}
/**
* Opens ByteArrayOutputStream for class data
*
* @return output stream
* @throws IOException
* never thrown
*/
@Override
public OutputStream openOutputStream() throws IOException {
return baos;
}
/**
* Throws {@link UnsupportedOperationException}
*
* @return nothing
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
throw new UnsupportedOperationException();
}
/**
* Throws {@link UnsupportedOperationException}
*
* @return nothing
*/
@Override
public Writer openWriter() throws IOException {
throw new UnsupportedOperationException();
}
/**
* Unsupported operation, always reutrns 0
*
* @return 0
*/
@Override
public long getLastModified() {
return 0;
}
/**
* Unsupported operation, returns false
*
* @return false
*/
@Override
public boolean delete() {
return false;
}
/**
* Returns true if {@link javax.tools.JavaFileObject.Kind#CLASS}
*
* @param simpleName
* doesn't matter
* @param kind
* kind to compare
* @return true if Kind is {@link javax.tools.JavaFileObject.Kind#CLASS}
*/
@Override
public boolean isNameCompatible(String simpleName, Kind kind) {
return Kind.CLASS.equals(kind);
}
/**
* Returns bytes of class
*
* @return bytes of class
*/
public byte[] getBytes() {
return baos.toByteArray();
}
/**
* Returns class that was loaded from binary data of this object
*
* @return loaded class
*/
public Class<?> getDefinedClass() {
return definedClass;
}
/**
* Sets class that was loaded by this object
*
* @param definedClass
* class that was loaded
*/
public void setDefinedClass(Class<?> definedClass) {
this.definedClass = definedClass;
}
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
return new InputStreamReader(new ByteArrayInputStream(getBytes()));
}
@Override
public Modifier getAccessLevel() {
return Modifier.PUBLIC;
}
@Override
public Kind getKind() {
return Kind.CLASS;
}
@Override
public NestingKind getNestingKind() {
return null;
}
}

View File

@@ -0,0 +1,208 @@
/*
* 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.javacc;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
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.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 ClassFileManager extends
ForwardingJavaFileManager<JavaFileManager> {
/**
* This map contains classes compiled for this classloader
*/
private final Map<String, BinaryClass> compiledClasses = CollectionFactory
.newMap(String.class, BinaryClass.class);
/**
* Classloader that will be used to load compiled classes
*/
protected ScriptClassLoaderImpl 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 ClassFileManager(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 ScriptClassLoaderImpl getClassLoader(Location location) {
if (loader == null) {
if (parentClassLoader != null) {
loader = new ScriptClassLoaderImpl(this, parentClassLoader);
} else {
loader = new ScriptClassLoaderImpl(this);
}
}
return loader;
}
/**
* Sets paraentClassLoader for this classLoader
*
* @param classLoader
* parent class loader
*/
public 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 {
ScriptClassLoaderImpl 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 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 = new ArrayList<JavaFileObject>();
for (JavaFileObject object : objects) {
temp.add(object);
}
temp.addAll(loader.getClassesForPackage(packageName));
objects = temp;
}
return objects;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.javacc;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class is simple compiler error listener that forwards errors to log4j
* logger
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ErrorListener implements DiagnosticListener<JavaFileObject> {
/**
* Logger for this class
*/
private static final Logger log = LoggerFactory
.getLogger(ErrorListener.class);
/**
* Reports compilation errors to log4j
*
* @param diagnostic
* compiler errors
*/
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
StringBuffer buffer = new StringBuffer();
buffer.append("Compier Error Report Start").append("\n");
buffer.append("errcode:").append(diagnostic.getCode()).append("\n");
buffer.append("line :").append(diagnostic.getLineNumber())
.append("\n");
buffer.append("column :").append(diagnostic.getColumnNumber())
.append("\n");
buffer.append("message:")
.append(diagnostic.getMessage(Locale.getDefault()))
.append("\n");
buffer.append("Compier Error Report End");
log.error(buffer.toString());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.javacc;
import java.io.File;
import java.io.IOException;
import javax.tools.SimpleJavaFileObject;
import org.apache.commons.io.FileUtils;
/**
* This class is simple wrapper for SimpleJavaFileObject that load class source
* from file sytem
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class JavaSourceFromFile extends SimpleJavaFileObject {
/**
* Construct a JavaFileObject of the given kind and with the given File.
*
* @param file
* the file with source of this file object
* @param kind
* the kind of this file object
*/
public JavaSourceFromFile(File file, Kind kind) {
super(file.toURI(), kind);
}
/**
* Returns class source represented as string.
*
* @param ignoreEncodingErrors
* not used
* @return class source
* @throws IOException
* if something goes wrong
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
return FileUtils.readFileToString(new File(this.toUri()));
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.javacc;
import java.net.URI;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
/**
* This class allows us to compile sources that are located only in memory.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class JavaSourceFromString extends SimpleJavaFileObject {
/**
* Source code of the class
*/
private final String code;
/**
* Creates new object that contains sources of java class
*
* @param className
* class name of class
* @param code
* source code of class
*/
public JavaSourceFromString(String className, String code) {
super(URI.create("string:///" + className.replace('.', '/')
+ JavaFileObject.Kind.SOURCE.extension),
JavaFileObject.Kind.SOURCE);
this.code = code;
}
/**
* Returns class source code
*
* @param ignoreEncodingErrors
* not used
* @return class source code
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}

View File

@@ -0,0 +1,289 @@
/*
* 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.javacc;
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.HashSet;
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.util.ClassUtils;
/**
* 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 ScriptClassLoaderImpl extends ScriptClassLoader {
/**
* Logger
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptClassLoaderImpl.class);
/**
* URL Stream handler to allow valid url generation by
* {@link #getResource(String)}
*/
private final VirtualClassURLStreamHandler urlStreamHandler = new VirtualClassURLStreamHandler(
this);
/**
* ClassFileManager that is related to this ClassLoader
*/
private final ClassFileManager 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 = new HashSet<String>();
/**
* Creates new ScriptClassLoader with given ClassFileManger
*
* @param classFileManager
* classFileManager of this classLoader
*/
ScriptClassLoaderImpl(ClassFileManager 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
*/
ScriptClassLoaderImpl(ClassFileManager classFileManager, ClassLoader parent) {
super(new URL[] {}, parent);
this.classFileManager = classFileManager;
}
/**
* Returns ClassFileManager that is related to this ClassLoader
*
* @return classFileManager of this classLoader
*/
public ClassFileManager getClassFileManager() {
return classFileManager;
}
/**
* AddsLibrary jar
*
* @param file
* jar file to add
* @throws IOException
*/
@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, 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,
VirtualClassURLStreamHandler.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 = new HashSet<JavaFileObject>();
// load parent
ClassLoader parent = getParent();
if (parent instanceof ScriptClassLoaderImpl) {
ScriptClassLoaderImpl pscl = (ScriptClassLoaderImpl) 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,253 @@
/*
* 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.javacc;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
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.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;
/**
* Wrapper for JavaCompiler api
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ScriptCompilerImpl implements ScriptCompiler {
/**
* Logger for this class
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptCompilerImpl.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 ScriptCompilerImpl() {
this.javaCompiler = ToolProvider.getSystemJavaCompiler();
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 setLibraires(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, String sourceCode) {
return compile(new String[] { className }, new String[] { 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, String[] sourceCode)
throws IllegalArgumentException {
if (classNames.length != sourceCode.length) {
throw new IllegalArgumentException(
"Amount of classes is not equal to amount of sources");
}
List<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
for (int i = 0; i < classNames.length; i++) {
JavaFileObject compilationUnit = new JavaSourceFromString(
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 = new ArrayList<JavaFileObject>();
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();
ClassFileManager manager = new ClassFileManager(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);
}
/**
* Reolves 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

@@ -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.javacc;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* This class represents URL Connection that is used to "connect" to scripts
* binary data that was loaded by specified
* {@link ScriptCompilerImpl}.<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 {
/**
* 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 VirtualClassURLConnection(URL url, ScriptClassLoaderImpl 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,66 @@
/*
* 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.javacc;
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 VirtualClassURLStreamHandler 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 ScriptClassLoaderImpl cl;
/**
* Creates new instance of url stream handler with given classloader
*
* @param cl
* ScriptClassLoaderImpl that was used to load compiled class
*/
public VirtualClassURLStreamHandler(ScriptClassLoaderImpl 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 VirtualClassURLConnection(u, cl);
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.metadata;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.l2jserver.service.game.scripting.ScriptContext;
import com.l2jserver.service.game.scripting.classlistener.DefaultClassListener;
/**
* Method marked as {@link OnClassLoad} will be called when class was loaded by
* script.<br>
* It's more useful alternative for
*
* <pre>
* static {
* ...
* }
* </pre>
*
* block.<br>
* <br>
* Only static methods with no arguments can be marked with this annotation.<br>
*
* This is only used if
* {@link ScriptContext#getClassListener()}
* returns
* {@link DefaultClassListener}
* instance.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClassLoad {
}

View File

@@ -0,0 +1,46 @@
/*
* 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.metadata;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.l2jserver.service.game.scripting.ScriptContext;
import com.l2jserver.service.game.scripting.classlistener.DefaultClassListener;
/**
* Method marked as {@link OnClassUnload} will be called when there is a script
* reload or shutdown.<br>
* Only static methods with no arguments can be marked with this annotation.<br>
*
* This is only used if
* {@link ScriptContext#getClassListener()}
* returns
* {@link DefaultClassListener}
* instance.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClassUnload {
}

View File

@@ -0,0 +1,173 @@
/*
* 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.scriptmanager;
import java.io.File;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
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;
/**
* Simple class that represents script info.<br>
* <br>
* It contains Script root, list of libraries and list of child contexes
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@XmlRootElement(name = "scriptinfo")
@XmlAccessorType(XmlAccessType.NONE)
public class ScriptInfo {
/**
* Root of this script context. Child directories of root will be scanned
* for script files
*/
@XmlAttribute(required = true)
private File root;
/**
* List of libraries of this script context
*/
@XmlElement(name = "library")
private List<File> libraries;
/**
* List of child contexts
*/
@XmlElement(name = "scriptinfo")
private List<ScriptInfo> scriptInfos;
/**
* Default compiler class name.
*/
@XmlElement(name = "compiler")
private String compilerClass = ScriptCompilerImpl.class.getName();
/**
* Returns root of script context
*
* @return root of script context
*/
public File getRoot() {
return root;
}
/**
* Sets root for script context
*
* @param root
* root for script context
*/
public void setRoot(File root) {
this.root = root;
}
/**
* Returns list of libraries that will be used byscript context and it's
* children
*
* @return lib of libraries
*/
public List<File> getLibraries() {
return libraries;
}
/**
* Sets list of libraries that will be used by script context and it's
* children
*
* @param libraries
* sets list of libraries
*/
public void setLibraries(List<File> libraries) {
this.libraries = libraries;
}
/**
* Return list of child context descriptors
*
* @return list of child context descriptors
*/
public List<ScriptInfo> getScriptInfos() {
return scriptInfos;
}
/**
* Sets list of child context descriptors
*
* @param scriptInfos
* list of child context descriptors
*/
public void setScriptInfos(List<ScriptInfo> scriptInfos) {
this.scriptInfos = scriptInfos;
}
/**
* Returns compiler class name
*
* @return name of compiler class
*/
public String getCompilerClass() {
return compilerClass;
}
/**
* Sets compiler class name
*
* @param compilerClass
* name of compiler class
*/
public void setCompilerClass(String compilerClass) {
this.compilerClass = compilerClass;
}
/**
* Returns true if roots are quals
*
* @param o
* object to compare with
* @return true if this ScriptInfo and anothers ScriptInfo has same root
*/
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ScriptInfo that = (ScriptInfo) o;
return root.equals(that.root);
}
/**
* Returns hashcode of root
*
* @return hashcode of root
*/
@Override
public int hashCode() {
return root.hashCode();
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.scriptmanager;
import java.util.Set;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Root element for script descriptors
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
@XmlRootElement(name = "scriptlist")
@XmlAccessorType(XmlAccessType.NONE)
public class ScriptList {
/**
* List of Script descriptors
*/
@XmlElement(name = "scriptinfo", type = ScriptInfo.class)
private Set<ScriptInfo> scriptInfos;
/**
* Returns list of script descriptors
*
* @return list of script descriptors
*/
public Set<ScriptInfo> getScriptInfos() {
return scriptInfos;
}
/**
* Sets list of script descriptors
*
* @param scriptInfos
* lisft of script descriptors
*/
public void setScriptInfos(Set<ScriptInfo> scriptInfos) {
this.scriptInfos = scriptInfos;
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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.scriptmanager;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.service.game.scripting.ScriptContext;
import com.l2jserver.service.game.scripting.ScriptContextFactory;
/**
* Class that represents managers of script contexes. It loads, reloads and
* unload script contexes. In the future it may be extended to support
* programatic manipulation of contexes, but for now it's not needed.
*
* Example:
*
* <pre>
* ScriptManager sm = new ScriptManager();
* sm.load(new File(&quot;st/contexts.xml&quot;));
* ...
* sm.shutdown();
* </pre>
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ScriptManager {
/**
* Logger for script context
*/
private static final Logger log = LoggerFactory
.getLogger(ScriptManager.class);
/**
* Collection of script contexts
*/
private Set<ScriptContext> contexts = new HashSet<ScriptContext>();
/**
* Loads script contexes from descriptor
*
* @param scriptDescriptor
* xml file that describes contexes
* @throws Exception
* if can't load file
*/
public synchronized void load(File scriptDescriptor) throws Exception {
JAXBContext c = JAXBContext.newInstance(ScriptInfo.class,
ScriptList.class);
Unmarshaller u = c.createUnmarshaller();
ScriptList list = (ScriptList) u.unmarshal(scriptDescriptor);
for (ScriptInfo si : list.getScriptInfos()) {
ScriptContext context = createContext(si, null);
if (context != null) {
contexts.add(context);
context.init();
}
}
}
/**
* Creates new context and checks to not produce copies
*
* @param si
* script context descriptor
* @param parent
* parent script context
* @return created script context
* @throws Exception
* if can't create context
*/
private ScriptContext createContext(ScriptInfo si, ScriptContext parent)
throws Exception {
ScriptContext context = ScriptContextFactory.getScriptContext(
si.getRoot(), parent);
context.setLibraries(si.getLibraries());
context.setCompilerClassName(si.getCompilerClass());
if (parent == null && contexts.contains(context)) {
log.warn("Double root script context definition: "
+ si.getRoot().getAbsolutePath());
return null;
}
if (si.getScriptInfos() != null && !si.getScriptInfos().isEmpty()) {
for (ScriptInfo child : si.getScriptInfos()) {
createContext(child, context);
}
}
return context;
}
/**
* Initializes shutdown on all contexes
*/
public synchronized void shutdown() {
for (ScriptContext context : contexts) {
context.shutdown();
}
contexts.clear();
}
/**
* Reloads all contexes
*/
public synchronized void reload() {
for (ScriptContext context : contexts) {
context.reload();
}
}
/**
* Returns unmodifiable set with script contexes
*
* @return unmodifiable set of script contexes
*/
public synchronized Collection<ScriptContext> getScriptContexts() {
return Collections.unmodifiableSet(contexts);
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.util;
/**
* This class contains utilities that are used when we are working with classes
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class ClassUtils {
/**
* Return true if class a is either equivalent to class b, or if class a is
* a subclass of class b, i.e. if a either "extends" or "implements" b. Note
* tht either or both "Class" objects may represent interfaces.
*
* @param a
* class
* @param b
* class
* @return true if a == b or a extends b or a implements b
*/
public static boolean isSubclass(Class<?> a, Class<?> b) {
// We rely on the fact that for any given java class or
// primtitive type there is a unqiue Class object, so
// we can use object equivalence in the comparisons.
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
for (Class<?> x = a; x != null; x = x.getSuperclass()) {
if (x == b) {
return true;
}
if (b.isInterface()) {
Class<?> interfaces[] = x.getInterfaces();
for (Class<?> anInterface : interfaces) {
if (isSubclass(anInterface, b)) {
return true;
}
}
}
}
return false;
}
/**
* Checks if class in member of the package
*
* @param clazz
* class to check
* @param packageName
* package
* @return true if is member
*/
public static boolean isPackageMember(Class<?> clazz, String packageName) {
return isPackageMember(clazz.getName(), packageName);
}
/**
* Checks if classNames belongs to package
*
* @param className
* class name
* @param packageName
* package
* @return true if belongs
*/
public static boolean isPackageMember(String className, String packageName) {
if (!className.contains(".")) {
return packageName == null || packageName.isEmpty();
} else {
String classPackage = className.substring(0,
className.lastIndexOf('.'));
return packageName.equals(classPackage);
}
}
}

View File

@@ -4,9 +4,10 @@ import junit.framework.Assert;
import org.junit.Test;
import script.dao.mysql5.DAOModuleMySQL5;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.l2jserver.db.dao.mysql5.DAOModuleMySQL5;
import com.l2jserver.model.id.CharacterID;
import com.l2jserver.model.id.ID;
import com.l2jserver.model.world.L2Character;

View File

@@ -7,11 +7,12 @@ import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import script.dao.mysql5.DAOModuleMySQL5;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
import com.l2jserver.db.dao.mysql5.DAOModuleMySQL5;
import com.l2jserver.model.id.factory.CharacterIDFactory;
import com.l2jserver.model.id.factory.IDFactoryModule;
import com.l2jserver.model.id.factory.ItemIDFactory;