Package net.ex337.scriptus.model

Source Code of net.ex337.scriptus.model.ScriptProcess

package net.ex337.scriptus.model;

import java.io.Serializable;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.Callable;

import javax.annotation.Resource;

import net.ex337.scriptus.ScriptusFacade;
import net.ex337.scriptus.config.ScriptusConfig;
import net.ex337.scriptus.config.ScriptusConfig.TransportType;
import net.ex337.scriptus.datastore.ScriptusDatastore;
import net.ex337.scriptus.datastore.impl.BaseScriptusDatastore;
import net.ex337.scriptus.exceptions.ScriptusRuntimeException;
import net.ex337.scriptus.model.api.ScriptusAPI;
import net.ex337.scriptus.model.api.Termination;
import net.ex337.scriptus.model.api.output.ErrorTermination;
import net.ex337.scriptus.model.api.output.NormalTermination;
import net.ex337.scriptus.model.support.ScriptusClassShutter;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContinuationPending;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;

/**
* Represents one script process. The source of the process
* is loaded at initialisation time and is kept with the
* process, i.e. changing a script will have no effect on
* currently executing scripts.
*
* @author ian
*
*/
public class ScriptProcess implements Callable<ScriptAction>, Runnable, Serializable, Cloneable {

  private static final Log LOG = LogFactory.getLog(ScriptProcess.class);

  /**
   * The length of the ID in bytes.
   */
  public static final int ID_SIZE_BYTES = 16;

  private static final long serialVersionUID = -7512596370437192858L;

  private UUID pid;
  private UUID waiterPid;
  private String userId;
  private String source;
  private String sourceName;
  private String args;
  private String owner;
  private Object state;
  private int version;
  private Function compiled;
  private boolean isRoot;
 
  private transient Object continuation;
  private transient Scriptable globalScope;
 
    private boolean isKilled;
    private boolean isAlive;
 
    private Date lastmod;
    private Date created;
   
    private TransportType transport;
 
  @Resource(name="datastore")
  private transient ScriptusDatastore datastore;

  @Resource
  private transient ScriptusConfig config;
 
  @Resource
  private transient ScriptusFacade facade;
 
  public ScriptProcess() {
  }

  /**
   *
   * Initialises a process using the supplied source from the given user ID.
   *
   * @param userId
   * @param sourceName
   * @param args
   * @param owner
   */
  public void init(String userId, final String sourceName, boolean sample, String args, String owner, TransportType transport) {

    LOG.debug("ctor, source=" + sourceName);

//    this.datastore = datastore;
    this.userId = userId;
    this.sourceName = sourceName;
    this.args = args;
    this.owner = owner;
    this.transport = transport;
   
    String sourceOwner = userId;
    if(sample){
        sourceOwner = BaseScriptusDatastore.SAMPLE_USER;
    }
   
    this.source = datastore.loadScriptSource(sourceOwner, sourceName);
    this.isRoot = true;
    this.isAlive = true;
    this.version = 0;

    Context cx = Context.enter();
    cx.setClassShutter(new ScriptusClassShutter());
    cx.setOptimizationLevel(-1); // must use interpreter mode
   
    try {

      ScriptusAPI scriptusApi = new ScriptusAPI(config);

      Scriptable globalScope = scriptusApi.createScope(cx);
     
      setGlobalScope(globalScope);
     
      compiled = cx.compileFunction(globalScope, "function ___scriptus___ () {"+source+"}", sourceName, 0, null);

    } catch (ScriptusRuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new ScriptusRuntimeException(e);
    } finally {
      Context.exit();
    }

  }

  /**
   * Writes the script state to DAO. If the pid is null we assign a new one.
   */
  public void save() {
      datastore.writeProcess(this);
  }

  /**
   *
   * Executes the script and returns the ScriptAction of the next API call, or a {@link Termination}
   * if the script finished.
   *
   */
  public ScriptAction call() {

    Object result;

    try {

      if (continuation == null) {

        LOG.debug("starting new script");

        Context cx = Context.enter();
        cx.putThreadLocal("process", this);
        cx.setClassShutter(new ScriptusClassShutter());

        globalScope.put("args", globalScope, Context.javaToJS(args, globalScope));
        globalScope.put("owner", globalScope, Context.javaToJS(owner, globalScope));

        try {
          // running for first time
          result = cx.callFunctionWithContinuations(compiled, globalScope, new Object[0]);
        } finally {
          Context.exit();

        }

      } else {

        LOG.debug("continuing existing script " + getPid().toString().substring(30));

        if (state instanceof ConvertsToScriptable) {
          state = ((ConvertsToScriptable) state).toScriptable();
        }

        Context cx = Context.enter();
        cx.setClassShutter(new ScriptusClassShutter());
        cx.putThreadLocal("process", this);
        try {
//            if(state != null) {
//                      System.out.println("state class="+state.getClass());
//            }
          result = cx.resumeContinuation(continuation, globalScope, state);
        } finally {
          Context.exit();
        }

      }

    } catch (RhinoException e) {

      LOG.error("script error", e);

      return new ErrorTermination(e);

    } catch (ContinuationPending cp) {

      continuation = cp.getContinuation();

      state = cp.getApplicationState();

      LOG.info("script continuation, state obj=" + state.getClass());

      if (state instanceof ScriptAction) {
        return (ScriptAction) state;
      } else {
        throw new ScriptusRuntimeException("Continuation state not ScriptAction:" + state);
      }

    }

    state = result;

    return new NormalTermination(result);

  }


  /**
   * Does the same as call() above, but also saves the process and calls visit()
   * on the resulting {@link ScriptAction}.
   */
  @Override
  public void run() {
   
    ScriptAction result = this.call();
   
    if(isKilled) {
      return;
     
    }

    this.save();
   
    result.visit(facade, this);
   
   
  }
 
  /**
   * If the process is running when kill() is called,
   * this method stops the next continuation from
   * executing.
   */
  public void kill() {
    isKilled = true;
  }

  /**
   * Copies the process, used in fork()ing.
   * Not a complete clone, the differences are:
   *  - isRoot is false
   *  - version is 0
   *  - pid & waiterPid is null
   *  - children is empty
   *
   */
  public ScriptProcess clone() {
    ScriptProcess r = new ScriptProcess();
    r.args = this.args;
    r.compiled = this.compiled;
    r.continuation = this.continuation;
    r.globalScope = this.globalScope;
    r.source = this.source;
    r.sourceName = this.sourceName;
    r.state = this.state;
    r.userId = this.userId;
    r.datastore = this.datastore;
    r.config = this.config;
    r.owner = this.owner;
    r.facade = this.facade;
    r.transport  = this.transport;
    // ?
    r.isRoot = false;
    r.isAlive = true;
    r.version = 0;
    r.pid = null;
    r.waiterPid = null;

    return r;

  }

  /**
   * deletes the script from DAO.
   */
  public void delete() {
   
    /*
     * TODO should this recursively delete child processes?
     *
     */
   
    datastore.deleteProcess(getPid());
  }

 
  public String getSource() {
    return source;
  }

  public Object getContinuation() {
    return continuation;
  }

  public Object getState() {
    return state;
  }

  public void setState(Object state) {
    this.state = state;
  }

  public void setArgs(String args) {
    this.args = args;
  }

  public UUID getWaiterPid() {
    return waiterPid;
  }

  public void setWaiterPid(UUID pid) {
    this.waiterPid = pid;
  }

  public String getArgs() {
    return args;
  }

  public Scriptable getGlobalScope() {
    return globalScope;
  }

  public void setSource(String source) {
    this.source = source;
  }

  public void setContinuation(Object continuation) {
    this.continuation = continuation;
  }

  public void setGlobalScope(Scriptable globalScope) {
    this.globalScope = globalScope;
  }

  public boolean equals(Object o) {
    return EqualsBuilder.reflectionEquals(this, o);
  }

  public UUID getPid() {
    return pid;
  }

  public void setPid(UUID pid) {
    this.pid = pid;
  }


  public String getOwner() {
    return owner;
  }

  public void setOwner(String owner) {
    this.owner = owner;
  }


 


  public boolean isRoot() {
    return isRoot;
  }

  public void setRoot(boolean isRoot) {
    this.isRoot = isRoot;
  }

  public String getUserId() {
    return userId;
  }

  public String getSourceName() {
    return sourceName;
  }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public Function getCompiled() {
        return compiled;
    }

    public void setCompiled(Function compiled) {
        this.compiled = compiled;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setSourceName(String sourceName) {
        this.sourceName = sourceName;
    }

    public UUID getLastChild() {
        return datastore.getLastChild(this.pid);
    }

    public void addChild(UUID childPid) {
        /*
         * since the version is incremented when we save, this means it's
         * OK to use as a child sequence - they don't have to be contiguous
         */
        datastore.addChild(this.pid, childPid, version);
    }

    public Date getLastmod() {
        return lastmod;
    }

    public void setLastmod(Date lastmod) {
        this.lastmod = lastmod;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public boolean isAlive() {
        return isAlive;
    }

    public void setAlive(boolean isAlive) {
        this.isAlive = isAlive;
    }

    public TransportType getTransport() {
        return transport;
    }

    public void setTransport(TransportType transport) {
        this.transport = transport;
    }

}
TOP

Related Classes of net.ex337.scriptus.model.ScriptProcess

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.