Package org.persvr.data

Source Code of org.persvr.data.Method

package org.persvr.data;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.BaseFunction;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.persvr.datasource.DataSource;
import org.persvr.datasource.HttpJsonSource;
import org.persvr.remote.Client;
import org.persvr.rpc.RPCall;
import org.persvr.security.PermissionLevel;

public class Method extends BaseFunction {
  static ThreadLocal<Boolean> safeMode = new ThreadLocal<Boolean>();
  BaseFunction innerFunction;
  String source;
  String methodName;
  static ThreadLocal<LinkedList<Long>> currentTimings = new ThreadLocal<LinkedList<Long>>();
  static class Timing {
    long own;
    long total;
    int calls;
    long max;
  }
  public static boolean profiling = false;
  static Map<Method, Timing> timings;
  public static void startProfiling(){
    timings = new HashMap<Method, Timing>();
    profiling = true;
  }
  public Method(String methodName) {
    this.methodName = methodName;
  }
  public Method(BaseFunction innerFunction, String methodName) {
    super();
    this.innerFunction = innerFunction;
    // we no longer "strip" parent scopes
    // innerFunction.setParentScope(GlobalData.getGlobalScope());
    Object selfDefinedSource = innerFunction.get("source", innerFunction);
    this.source = selfDefinedSource instanceof String ? (String) selfDefinedSource : ScriptRuntime.toString(innerFunction);
    this.methodName = methodName;
    setPrototype(ScriptableObject.getClassPrototype(GlobalData.getGlobalScope(), "Function"));
    put("prototype", this, innerFunction.get("prototype", innerFunction));
  }
  @Override
  public Object call(Context cx, Scriptable scope,
      Scriptable thisObj, Object[] args) {
    return call(cx, scope, thisObj, args, false);
  }
  String className;
  public Object call(Context cx, Scriptable scope,
      Scriptable thisObj, Object[] args, boolean clientInitiatedCall) {
    if(!(thisObj instanceof Persistable)){
      return innerFunction.call(cx, scope, thisObj, args);
    }
    //record the current security level
    boolean security = PersistableObject.isSecurityEnabled();
    if(security){
      PersistableObject.checkSecurity((Persistable)thisObj, PermissionLevel.EXECUTE_LEVEL.level)
    }
    // go into unsecure mode, the code can run with full priviledges
    PersistableObject.enableSecurity(false);
    long startTime = 0;
    try{
     
      if(profiling){
        try {
          if(className == null) {
            if(thisObj instanceof PersistableClass)
              className = ((Persistable)thisObj).getId().subObjectId;
            else {
              Persistable clazz = thisObj instanceof SchemaObject ?
                      ((Persistable) thisObj).getParent() :
                        ((Persistable) thisObj).getSchema();
              if(clazz == null)
                className = "";
              else
                className = clazz.getId().subObjectId;
            }
          }
          startTime = startTiming();
        } catch (RuntimeException e) {
          LogFactory.getLog(Method.class).error(e);
        }
      }
      return callWithChecks(cx, scope, thisObj, args, clientInitiatedCall);
    }
    finally{
      //restore the security level
      PersistableObject.enableSecurity(security);
      if(profiling){
        stopTiming(startTime, this);
      }

    }
  }
  public Object callWithChecks(Context cx, Scriptable scope,
    Scriptable thisObj, Object[] args, boolean clientInitiatedCall) {
    Persistable methodDefinition = FunctionUtils.getMethodDefinition((Persistable) thisObj, methodName);
    Object runAt = null;
    if (methodDefinition != null) {
      Object parameters = methodDefinition.get("parameters");
      if (parameters instanceof PersistableArray) {
        // check to make sure the parameters match the required parameters for the method definition
        int i = 0;
        for (Object parameter : (PersistableArray) parameters){
          Object arg = i < args.length ? args[i] : Undefined.instance;
          i++;
          try {
            PersistableClass.enforceSchemaForValue((Persistable) parameter, arg);
          }
          catch (EcmaError e) {
            throw PersistableClass.addPropertyToValidationError(e,"parameter " + i);
          }
        }
      }
      if(Boolean.TRUE == safeMode.get() && !Boolean.TRUE.equals(methodDefinition.get("safe")))
        throw new SecurityException("Can not call a method in a query unless it is marked 'safe' in the method definition");
      if(clientInitiatedCall && Boolean.TRUE.equals(methodDefinition.get("internal")))
        throw new SecurityException("Can not call a method marked 'internal' through a remote call");
      runAt = methodDefinition.get("runAt");
      if(((Persistable)thisObj).getId().source instanceof HttpJsonSource) {
        if(!("client".equals(runAt) || "system".equals(runAt) || "local".equals(runAt))){
            return ((HttpJsonSource)((Persistable)thisObj).getId().source).executeRPC(((Persistable)thisObj).getId(), methodName, args);
        }
      }
      else if ("client".equals(runAt)){
        Client.getCurrentObjectResponse().sendMessage(new RPCall((Persistable)thisObj,methodName,args));
        return RPCall.SUSPENDED;
      }
     
      Object returnValue = innerFunction.call(cx, scope, thisObj, args);
      Object returns = methodDefinition.get("returns");
      if (returns instanceof Persistable) {
        try {
          PersistableClass.enforceSchemaForValue((Persistable) returns, returnValue);
        }
        catch (EcmaError e) {
          throw PersistableClass.addPropertyToValidationError(e,"the return value");
        }
      }
      if(Boolean.TRUE.equals(methodDefinition.get("observable"))) {
        if (returnValue instanceof org.mozilla.javascript.Undefined && methodName.toUpperCase().equals("DELETE")) {
          Transaction.currentTransaction().addObservedCall(((Persistable)thisObj).getId(),innerFunction instanceof RestMethod ? methodName.toUpperCase() : methodName, thisObj, Boolean.TRUE.equals(methodDefinition.get("idempotent")), clientInitiatedCall);
        } else {
          Transaction.currentTransaction().addObservedCall(((Persistable)thisObj).getId(),innerFunction instanceof RestMethod ? methodName.toUpperCase() : methodName, returnValue, Boolean.TRUE.equals(methodDefinition.get("idempotent")), clientInitiatedCall);
        }
      }
      return returnValue;
    }
    if(((Persistable)thisObj).getId().source instanceof HttpJsonSource) {
      if(!("client".equals(runAt) || "system".equals(runAt) || "system".equals(runAt))){
          return ((HttpJsonSource)((Persistable)thisObj).getId().source).executeRPC(((Persistable)thisObj).getId(), methodName, args);
      }
    }
    return innerFunction.call(cx, scope, thisObj, args);
  }
  public static long startTiming(){
    long startTime = System.nanoTime();
    LinkedList<Long> currentTiming = currentTimings.get();
    if(currentTiming == null){
      currentTiming = new LinkedList<Long>();
      currentTimings.set(currentTiming);
    }
    if(!currentTiming.isEmpty()) {
      long lastTime = currentTiming.removeLast();
      currentTiming.add(startTime - lastTime);
    }
    currentTiming.add(startTime);
    return startTime;
  }
  public static void stopTiming(long startTime, Method method){
    LinkedList<Long> currentTiming = currentTimings.get();
    if(currentTiming != null){
      long now = System.nanoTime();
      Timing timing = timings.get(method);
      if(timing == null) {
        timing = new Timing();
        timings.put(method, timing);
      }
      timing.total += now - startTime;
      long own = now - currentTiming.removeLast();
      timing.own += own;
      timing.max = Math.max(timing.max, own);
      timing.calls++;
      if(!currentTiming.isEmpty()) {
        long lastTime = currentTiming.removeLast();
        currentTiming.add(now - lastTime);
      }
    }

  }
  public String toString(){
    return source;
  }
  public void setName(String name) {
    this.methodName = name;
  }
  public int getArity() {
    return innerFunction.getArity();
  }

  public String getClassName() {
    return innerFunction.getClassName();
  }

  public String getFunctionName() {
    return innerFunction.getFunctionName();
  }
  public Object getDefaultValue(Class typeHint){
    if (typeHint == ScriptRuntime.FunctionClass)
      return innerFunction;
    return super.getDefaultValue(typeHint);
  }

}
TOP

Related Classes of org.persvr.data.Method

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.