/* JythonTask.java This class acts as a proxy for a separate Builder Task (a class that handles processing Ganymede's data for use in DNS/LDAP/Mail server/etc.) that's written in Jython. This task takes one parameter, a URI that points to the actual Jython code. The code is downloaded, loaded, and executed as if it was any other Ganymede native-Java task. Created: 22 July 2004 Module By: Deepak Giridharagopal <deepak@arlut.utexas.edu> ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2013 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 This program 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 2 of the License, or (at your option) any later version. This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.server; import java.util.Vector; import org.python.core.PyException; import org.python.core.PySystemState; import org.python.util.PythonInterpreter; import arlut.csd.ganymede.common.Invid; import arlut.csd.ganymede.common.SchemaConstants; /*------------------------------------------------------------------------------ class JythonTask ------------------------------------------------------------------------------*/ /** * <p>This class acts as a proxy for a separate Builder Task (a class * that handles processing Ganymede's data for use in DNS/LDAP/Mail * server/etc.) that's written in Jython. This task doesn't do much on * its own; it simply bootstraps a Jython interpreter to a state where * it can execute a "proper" builder task written in Jython and stored * at an external location.</p> * * <p>Every JythonTask needs to be registered in the Ganymede database * via the task object type. They all require a single option string: * the URI of the "real" builder task that is written in Jython. The * URI can point to a resource using any protocol the Jython * <code>urllib</code> module can handle (http://, file://, ftp://, * etc).</p> * * <p>The "real" task, written in Jython, is ideally a subclass of * {@link arlut.csd.ganymede.server.GanymedeBuilderTask * GanymedeBuilderTask} (though it can simply be any class that * implements the {@link java.lang.Runnable Runnable} interface). This * class will bootstrap a new Jython interpreter, load the class * defined at the specified URI, and then execute that task's {@link * java.lang.Runnable#run()} method.</p> * * <p>It's important to mention that <b>no state is stored in-between * runs of a Jython task</b>. A new interpreter is initialized every * time the task is run, and that interpreter is not shared by any * other Jython task. So any variables that exist solely in the * context of the Jython interpreter should be considered * <b>transient</b> (not in the Java sense of the word, but in the * English sense of the word).</p> */ public class JythonTask implements Runnable { /** * <p>The invid of this actual task definition. This is needed so we * can snag this task's option strings from the database.</p> */ private Invid taskDefObjInvid; /* -- */ public JythonTask(Invid invid) { taskDefObjInvid = invid; } /** * <p>This method is the one invoked when the scheduler runs this * task. It creates a new Jython interpreter, runs the bootstrapping * code, and then executes the external Jython task.</p> */ public final void run() { String uri; PythonInterpreter interp; uri = getURI(); if (uri == null) { throw new RuntimeException("Unable to get URI of the the Jython code for this task."); } /* Initialize the interpreter */ interp = new PythonInterpreter(null, new PySystemState()); try { /* Import the additional Jython library routines */ interp.exec("import sys"); interp.exec("sys.path.append( sys.prefix + '" + System.getProperty("file.separator") + "' + 'jython-lib.jar' )"); /* Launch the actual task */ interp.exec("import JythonTaskBootstrapper"); interp.set("uri", uri); interp.exec("JythonTaskBootstrapper.run(uri)"); } catch (PyException pex) { throw new RuntimeException(pex.toString()); } interp = null; } /** * <p>Parses this task's option string to grab the URI of the * proxied Jython task. If there is more than one option string * defined, the first one wins.</p> * * @return The URI of the actual task (written in Jython) that does * the work */ public String getURI() { if (taskDefObjInvid != null) { DBObject taskDefObj = Ganymede.internalSession.getDBSession().viewDBObject(taskDefObjInvid); Vector<String> options = (Vector<String>) taskDefObj.getFieldValuesLocal(SchemaConstants.TaskOptionStrings); if (options.size() == 0) { return null; } return options.get(0); } return null; } }