com.l2jserver.service.game.scripting.impl.ScriptContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.l2jserver.service.game.scripting.impl.ScriptContextImpl.java

Source

/*
 * 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.Set;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Guice;
import com.google.inject.Injector;
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.ScriptContext;
import com.l2jserver.service.game.scripting.classlistener.ClassListener;
import com.l2jserver.service.game.scripting.classlistener.DefaultClassListener;
import com.l2jserver.util.factory.CollectionFactory;

/**
 * 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);

    /**
     * The {@link Guice} {@link Injector}
     */
    private final Injector injector;

    /**
     * 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 injector
     *            the Guice {@link Injector}
     * @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(Injector injector, File root) {
        this(injector, root, null);
    }

    /**
     * Creates new ScriptContext with given file as root and another
     * ScriptContext as parent
     * 
     * @param injector
     *            the Guice {@link Injector}
     * @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(Injector injector, 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.injector = injector;
        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();

        Collection<File> files = FileUtils.listFiles(root, scriptCompiler.getSupportedFileTypes(), true);

        if (parentScriptContext != null) {
            scriptCompiler.setParentClassLoader(parentScriptContext.getCompilationResult().getClassLoader());
        }

        scriptCompiler.setLibraries(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 synchronized Collection<ScriptContext> getChildScriptContexts() {
        return childScriptContexts;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void addChildScriptContext(ScriptContext context) {
        synchronized (this) {
            if (childScriptContexts == null) {
                childScriptContexts = CollectionFactory.newSet();
            }

            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(injector));
                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;
    }

    @Override
    public synchronized ScriptClassLoader getClassLoader() {
        return compilationResult.getClassLoader();
    }

    /**
     * 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
    protected void finalize() throws Throwable {
        if (compilationResult != null) {
            log.error("Finalization of initialized ScriptContext. Forcing context shutdown.");
            shutdown();
        }
        super.finalize();
    }
}