A couple of weeks ago, I was working on a Java application that creates directories and files based on the parameters given. Like with all of my applications, I want to know what’s going on while they are running. This got me thinking about my previous article that talked about a small logging module I wrote in Python. This module contains different levels for logging that get inserted into a database. The problem is that it was written in Python and that obviously doesn’t mix with Java. Or does it?
It took less than five minutes of Googling, to discover Jython. In a nutshell, Jython is an implementation of Python that runs on Java. For the most part, it is compatible with Python 2.7. Keep in mind that Python 2.7 is no longer receiving support, but the Jython team is working to get it compatible with Python 3.
Initially, I was hoping to leverage the Python logging class that I had already written. Essentially, this would require importing the module, creating a class object, and then calling one of the class functions (Depending on which level of logging I wanted to do). It would have looked like something similar to this:
from my_logger import MyLogger myLogger = MyLogger("project_creator", "mysql://<USER>:<PASSWORD>@<HOST>/<DATABASE>") myLogger.LogInfo("This is some logging information.")
Using Jython, this would loosely translate to:
PythonInterpreter interp = new PythonInterpreter(); interp.exec("from my_logger import MyLogger"); PyInstance loggingObject = (PyInstance)interp.eval("MyLogging('project_creator', 'mysql://<USER>:<PASSWORD>@<HOST>/<DATABASE>')"); System.out.println(loggingObject.invoke("LogInfo()").__tojava__(String.class));
Writing the Java code seemed fairly quick and easy. With my hopes high, I started researching what needs to be done to get this project to build and run. It was after quite a lot of time and experimenting where I realized that my original plan wasn’t going to work. Before I realized this, I needed to get Jython installed first.
Ubuntu has a package in its store called Jython that can be used. However, this package is for Jython 2.5. All of the modern documentation recommends downloading the Jython 2.7 installer which will give you the most recent version. Once downloaded, to run the installer:
java -jar jython-installer-2.7.2.jar
Selecting the second option which is the Standard edition will install the .jar file that you need along with demos and documentation. I tried the ninth option, which is supposed to install just the .jar file failed during installation every time I tried. While installing, you will be prompted to specify which directory you want the .jar file to install to.
Once installed, I was able to run this command that, in theory, would build my Java application:
javac -cp ~/jython/jython.jar jythontest/*.java
Unfortunately, that was wishful thinking. It was here that I started seeing errors for missing Python modules. After more research, I learned that I need to install the missing modules for Jython using the .jar file that I installed. Normal Python uses “pip” to install packages. From the research, I’ve done, Jython did have something called “jip”, but documentation for that is scarce. It was a little bit of a pain, but I had to download the missing modules from PyPi and unzip them. The unzipped module directories contain a setup.py file Using a command similar to this, I was able to install them:
java -jar ~/jython/jython.jar setup.py install
All seemed well until I tried building the project for a second time. Again, it was another missing module (_mysql to be exact). This led to another round of research as to why my project wouldn’t build. As it turns out, the driver in the connection string I was using for SQLAlchemy was causing the issue. Since Jython was built using Java, it relies on the JDBC for connections to MySQL. In short, the JDBC (Java Database Connectivity)is an API (Application Programming Interface)that tells the client how it can connect to a database.
JayDeBeApi and zxJDBC are a couple of modules that could be used to fix the problem, however, they don’t mix well with SQLAlchemy’s create_engine() function. By this, I mean when I tried using the recommended driver, “jdbc:mysql”, that the function fails to parse the URL. This is probably because these modules construct the connection in a different way than SQLAlchemy and are more designed for parameterized querying.
The last hope that I had was in the documentation found here. Reading through, it was very detailed and well written. There is an SQLAlchemy example, but it’s connecting to an Oracle database using the module zxoracle. After another trip to Google to look for something similar, I came across the Github repo for zxoracle. Scanning through this code, I think it would be possible to modify this code to be used for MySQL. In the end, I decided that I needed to alter the course I was taking with this project.
It wasn’t an easy decision by any means to change the implementation plan. Instead of importing the logging class and calling its functions, I decided to create an API for the logging class and create a client class that Jython will use. Since I already intended on making an API for logging, this wasn’t any extra work. Below is what the client class looked like and the Jython code in Java to call it:
Looking at the client.py file, a very simple function was written that creates a POST request that gets sent to the logging API. A dictionary that contains the application name, log message, and log event type gets sent along with the request. The Jython code in jython.java admittedly looks a little weird. What it basically does is import the Python file, create a function object, and pass the necessary parameters to that function. Just like earlier, the command I had to run that builds the Java project was:
javac -cp ~/jython/jython.jar jythontest/*.java
At last, I was finally able to build without getting any errors. To run the project, this command was used. Note that because my project required command-line parameters, it was necessary to add it in (the part that says “python”).
java -cp ~/jython/jython.jar:. jythontest.StartingPoint "python"
Once the project had finished running, I checked the database to make sure my log event had been inserted.
I think it was safe to say that at this point, I finally had a working project.
Reflecting on the events that occurred while trying to get the project to work, made me realize that maybe Jython isn’t the approach I’m looking for. On previous Java projects, I used the ProcessBuilder class to run all sorts of scripts. I am also very aware that Python has its own methods for calling APIs. I guess I was just hoping that Jython would save me a little more development time. In the end, I don’t regret the experience I obtained. If you happen to know what I did wrong while trying to get the SQLAlchemy driver to work, or notice any other mistakes I might have made, feel free to leave a comment. Happy coding and cheers!
Also published at https://python.plainenglish.io/java-python-jython-f887b356a92e