All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.vertx.java.platform.impl.JythonVerticleFactory Maven / Gradle / Ivy

/*
 * Copyright 2011-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.vertx.java.platform.impl;

import org.python.core.Options;
import org.python.core.PyException;
import org.python.core.PySystemState;
import org.python.util.PythonInterpreter;
import org.vertx.java.core.Vertx;
import org.vertx.java.core.VertxException;
import org.vertx.java.core.json.DecodeException;
import org.vertx.java.core.json.JsonObject;
import org.vertx.java.core.logging.Logger;
import org.vertx.java.platform.Container;
import org.vertx.java.platform.PlatformManagerException;
import org.vertx.java.platform.Verticle;
import org.vertx.java.platform.VerticleFactory;

import java.io.*;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Scott Horn
 * @author Tim Fox
 */
public class JythonVerticleFactory implements VerticleFactory {

  private ClassLoader cl;
  private PythonInterpreter py;
  private static final AtomicInteger seq = new AtomicInteger();

  public static Vertx vertx;
  public static Container container;

  public JythonVerticleFactory() {
  }

  @Override
  public void init(Vertx vertx, Container container, ClassLoader cl) {
    this.cl = cl;
    JythonVerticleFactory.vertx = vertx;
    JythonVerticleFactory.container = container;
    System.setProperty("python.options.internalTablesImpl","weak");
    Thread.currentThread().setContextClassLoader(cl);
    Options.includeJavaStackInExceptions = false;
    this.py = new PythonInterpreter(null, new PySystemState());
  }

  public Verticle createVerticle(String main) throws Exception {
    return new JythonVerticle(main);
  }

  public void reportException(Logger logger, Throwable t) {
    if (t.getClass().getCanonicalName().contains("PyException")) {
      /*
      We have to adjust the line numbers in the stack trace so they are accurate
      Unfortunately Jython doesn't allow us to retrieve the stack trace as an array making this
      ugly
      */
      StringWriter sw = new StringWriter();
      PrintWriter pw = new PrintWriter(sw);
      t.printStackTrace(pw);
      BufferedReader rdr =  new BufferedReader(new StringReader(sw.toString()));
      String line;
      StringBuilder newStack = new StringBuilder();
      try {
        while ((line = rdr.readLine()) != null) {
          String newLine;
          if (line.contains(".py") && !line.contains("__pyclasspath__") && line.contains(", in ")) {
            String lineNumber = line.substring(0, line.lastIndexOf(", in "));
            int pos = lineNumber.lastIndexOf(", line ") + 7;
            lineNumber = lineNumber.substring(pos);
            int lineNo = Integer.parseInt(lineNumber);
            newLine = line.substring(0, pos) + (lineNo - 1) + line.substring(pos + lineNumber.length());
          } else {
            newLine = line;
          }
          newStack.append(newLine).append("\n");
        }
      } catch (IOException e) {
        throw new RuntimeException("Failed to parse stack trace: " + e.getMessage());
      }
      logger.error("Exception in Python verticle: " + t.getMessage());
      logger.error(newStack);
    } else {
      logger.error("Exception in Python verticle", t);
    }
  }

  public void close() {
    py.cleanup();
  }

  private class JythonVerticle extends Verticle {

    private final String scriptName;
    private String funcName;
    private StringBuilder stopFuncName;
    private StringBuilder stopFuncVar;

    JythonVerticle(String scriptName) {
      this.scriptName = scriptName;
    }

    public void start() {
      try (InputStream is = cl.getResourceAsStream(scriptName)) {
        if (is == null) {
          throw new IllegalArgumentException("Cannot find verticle: " + scriptName);
        }
        // We wrap the python verticle in a function so different instances don't see each others top level vars
        String genName = "__VertxInternalVert__" + seq.incrementAndGet();
        funcName = "f" + genName;
        StringBuilder sWrap = new StringBuilder("def ").append(funcName).append("():\n");
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        for (String line = br.readLine(); line != null; line = br.readLine()) {
          // Append line indented by a tab
          sWrap.append("\t").append(line).append("\n");
        }
        br.close();
        // The return value of the wrapping function is the vertx_stop function (if defined)
        sWrap.append("\tif 'vertx_stop' in locals():\n");
        sWrap.append("\t\treturn vertx_stop\n");
        sWrap.append("\telse:\n");
        sWrap.append("\t\treturn None\n");

        // And then we have to add a top level wrapper method that calls the actual vertx_stop method
        stopFuncVar = new StringBuilder("v").append(genName);
        sWrap.append(stopFuncVar).append(" = ").append(funcName).append("()\n");
        stopFuncName = new StringBuilder(funcName).append("_stop");
        sWrap.append("def ").append(stopFuncName).append("():\n");
        sWrap.append("\tif ").append(stopFuncVar).append(" is not None:\n");
        sWrap.append("\t\t").append(stopFuncVar).append("()\n");



        // We have to convert it back to an inputstream since for some reason there is no version
        // py.exec which takes a String AND a fileName - and without the filename
        // any stack traces from errors won't show the filename and be hard for the user to parse.
        try (InputStream sis = new ByteArrayInputStream(sWrap.toString().getBytes("UTF-8"))) {
          py.execfile(sis, scriptName);
        }

      } catch (Exception e) {
        funcName = null;
        stopFuncName = null;
        stopFuncVar = null;
        throw new VertxException(e);
      }
    }

    public void stop() {
      if (stopFuncName != null) {
        py.exec(stopFuncName.toString() + "()");
        // And delete the globals
        py.exec("del " + stopFuncVar);
        py.exec("del " + stopFuncName);
        py.exec("del " + funcName);
      }
    }
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy