package geniusweb.pythonadapter;
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PySystemState;
import org.python.util.PythonInterpreter;
import geniusweb.actions.Action;
import geniusweb.connection.ConnectionEnd;
import geniusweb.inform.Inform;
import geniusweb.party.Capabilities;
import geniusweb.party.DefaultParty;
import geniusweb.party.Party;
import tudelft.utilities.listener.Listener;
/**
* An adapter that allows you to put a python-based Party into a jar file and
* use it with Genius2. This adapter supports the jython dialect. The reason
* this adapter is needed is that you can not directly use the .py file in java,
* we have to load a python interpreter and connect it to your file. *
*
Usage
*
*
* - Place an implementation of this class (PythonPartyAdapter) inside your
* jar.
*
- Implement {@link #getPythonClass()} so that it returns the proper name of
* your implementation.
*
- Point to this implementation as the mainclass in the META-INF file. Check
* the RandomPyParty example on how to do this from Maven.
*
- Ensure your Python file(s) get in the root of the jar. Eg, put them in
* resources/ or similar inside your maven project if you use maven.
*
- Add more dependencies as needed, eg to access the Bid class or for
* deserializing.
*
- Make sure you use the jar-with-dependencies in the parties server, as
* that includes jython.
*
*
* Working principle
*
* The created jar file contains a full python interpreter plus some glue code.
* The glue code runs your party inside the python interpreter and routes the
* calls through the Geniuseb java structures.
*/
public abstract class PythonPartyAdapter implements Party, Listener {
private final PythonInterpreter interpreter = new PythonInterpreter();
private final DefaultParty pyparty;
public PythonPartyAdapter() {
/**
* Normally Party does not have a constructor. However this is a special
* case where we need to initialize Python and connect the python class.
*/
interpreter.exec("import sys");
addPath("");
System.out.println("code is at " + getClass().getProtectionDomain()
.getCodeSource().getLocation().getPath());
// point to sources. Should include all dependencies.
// should work both in Eclipse and when using the compiled jar.
addPath(getClass().getProtectionDomain().getCodeSource().getLocation()
.getPath());
PySystemState state = new PySystemState();
PyObject importer = state.getBuiltins()
.__getitem__(Py.newString("__import__"));
PyObject module = importer.__call__(Py.newString(getPythonClass()));
PyObject klass = module.__getattr__(getPythonClass());
pyparty = (DefaultParty) klass.__call__()
.__tojava__(DefaultParty.class);
}
/**
* @return the class/modulename of both your python module filename (eg,
* X.py) and your python class (eg class X) inside that file that
* implements {@link DefaultParty}. This file must be in the root of
* the jar file/working directory. The name is without an extension.
*/
abstract public String getPythonClass();
@Override
public Capabilities getCapabilities() {
return pyparty.getCapabilities();
}
@Override
public String getDescription() {
return pyparty.getDescription();
}
@Override
public void notifyChange(Inform data) {
pyparty.notifyChange(data);
}
@Override
public void disconnect() {
pyparty.disconnect();
}
@Override
public void terminate() {
pyparty.terminate();
}
@Override
public void connect(ConnectionEnd connection) {
pyparty.connect(connection);
}
private void addPath(String relpath) {
String path = System.getProperty("user.dir") + "/" + relpath + "/";
System.out.println("adding python path " + path);
interpreter.exec("sys.path.insert(0, '" + path + "')");
}
}