Package org.moyrax.javascript

Source Code of org.moyrax.javascript.ConfigurableEngine

package org.moyrax.javascript;

import java.util.ArrayList;
import java.util.List;

import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.JavaScriptException;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;

import org.apache.commons.lang.Validate;
import org.moyrax.util.ScriptUtils;

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;

/**
* This {@link JavaScriptEngine} allows dynamically to configure the pages's
* scope, extending the basic HtmlUnit host's features.
*
* @author Matias Mirabelli <lumen.night@gmail.com>
* @since 1.2
*/
public class ConfigurableEngine extends JavaScriptEngine {
  /** Default ID for serialization. */
  private static final long serialVersionUID = 1L;

  /**
   * List of beans registered in the executions scope.
   */
  private List<ScriptComponent> components;

  /**
   * Global context, shared by all windows.
   */
  private Context globalContext;

  /**
   * List of JavaScript resources which will be executed at scope level. All
   * of these resources will be loaded before executing the code inside the
   * scope.
   */
  private ArrayList<String> scopeResources = new ArrayList<String>();

  /**
   * Creates a new {@link ConfigurableEngine} and sets the enclosing
   * {@link WebClient}.
   *
   * @param theWebClient The web client that will use this engine. It cannot
   *    be null.
   */
  public ConfigurableEngine(final WebClient theWebClient) {
    super(theWebClient);

    // Entering to the context, Rhino binds the context to this thread.
    // If there's no context binded to the thread, Rhino will create a new
    // temporary context each time an action is going to be executed.
    // We need to keep the context in order to load the scope resources.
    globalContext = getContextFactory().enterContext();
  }

  /**
   * Executes the specified JavaScript code in the scope of the given page.
   *
   * @param htmlPage The page in which the code resides.
   * @param sourceCode The JavaScript code to be executed.
   * @param sourceName The name of the source file.
   * @param startLine The line in which the code will start the execution.
   *
   * @return Returns the result of the script execution.
   * @throws JavaScriptException if there are any errors in the script
   *    execution.
   * @throws JavaScriptEngineException If there are errors in the environment
   *    configuration.
   */
  @Override
  public Object execute(final HtmlPage htmlPage, final String sourceCode,
      final String sourceName, final int startLine)
        throws JavaScriptException, JavaScriptEngineException {

    final ScriptableObject scope = (ScriptableObject)htmlPage
        .getEnclosingWindow().getScriptObject();

    this.initializeScope(scope);
    this.loadScopeResources(scope);

    return super.execute(htmlPage, sourceCode, sourceName, startLine);
  }

  /**
   * Registers a new {@link ScriptableObject} class that will be available in
   * the execution scopes created by this engine.
   *
   * @param klass Class to register in all scopes. It cannot be null.
   */
  public void registerClass(final Class<?> klass) {
    registerClass(klass, getClass().getClassLoader());
  }

  /**
   * Registers a new {@link ScriptableObject} class that will be available in
   * the execution scopes created by this engine.
   *
   * @param klass Class to register in all scopes. It cannot be null.
   * @param classLoader {@link ClassLoader} used to locate the class related
   *    resources. It cannot be null.
   */
  public void registerClass(final Class<?> klass,
      final ClassLoader classLoader) {
    Validate.notNull(klass, "The class cannot be null.");
    Validate.notNull(classLoader, "The class loader cannot be null.");

    if (components == null) {
      components = new ArrayList<ScriptComponent>();
    }

    components.add(new ScriptComponent(klass, classLoader));
  }

  /**
   * Adds a new resource which will be registered in the Window scope. It's
   * useful to initialize the client environment before executing the tests.
   *
   * @param classPath Resource located in the classpath. It cannot be null or
   *    empty.
   */
  public void addGlobalResource(final String classPath) {
    Validate.notEmpty(classPath, "The resource classpath cannot be null.");

    scopeResources.add(classPath);
  }

  /**
   * Initializes the specified {@link Scriptable} object adding all registered
   * classes to the scope.
   *
   * @param scope Scope to initialize. It cannot be null.
   */
  @SuppressWarnings("unchecked")
  private void initializeScope(final ScriptableObject scope) {
    Validate.notNull(scope, "The scope cannot be null.");

    /* Registers all global functions. */
    for (ScriptComponent bean : components) {
      String[] globalFunctions = bean.getGlobalFunctionNames().toArray(
          new String[] {});

      /* Adds the global functions to the scope. */
      scope.defineFunctionProperties(globalFunctions,
          bean.getScriptableClass(), ScriptableObject.DONTENUM);
    }

    try {
      /* Tries to register the defined classes. */
      for (ScriptComponent bean : components) {
        ScriptableObject.defineClass(scope,
            (Class<? extends ScriptableObject>)bean.getScriptableClass());
      }
    } catch (Exception ex) {
      throw new JavaScriptEngineException("Error initializing the scope.", ex);
    }
  }

  /**
   * Executes the configured global resources in the given scope.
   *
   * @param scope Scope to load the configured resources. It cannot be null.
   */
  private void loadScopeResources(final ScriptableObject scope) {
    Validate.notNull(scope, "The scope cannot be null.");

    for (String classPath : scopeResources) {
      ScriptUtils.run(globalContext, scope, classPath);
    }
  }
}
TOP

Related Classes of org.moyrax.javascript.ConfigurableEngine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.