Jython is an implementation of the Python programming language that runs on the Java Virtual Machine (JVM). It allows developers to write Python code that can seamlessly interact with Java classes and libraries. This creates a powerful bridge between Python’s simplicity and Java’s extensive ecosystem. You can host Jython apps using any hosting package from JVMHost.com. They all include a private JVM and the entry package can ordered here.
Below is an example (SendMailServlet.py) using Jython with a simple servlet. The servlet serves a form and processes form submissions to send an email.
SendMailServlet.py# -*- coding: utf-8 -*-
from javax.servlet.http import HttpServlet
from javax.servlet.http import HttpServletRequest, HttpServletResponse
from javax.mail import Message, Session, Transport
from javax.mail.internet import InternetAddress, MimeMessage
from java.util import Properties
from java.io import FileInputStream
class SendMailServlet(HttpServlet):
def doGet(self, request, response):
response.setContentType("text/html;charset=UTF-8")
out = response.getWriter()
out.println("""
<html><body>
<h2>Send Email</h2>
<form method='POST'>
To: <input type='text' name='to'><br><br>
Subject: <input type='text' name='subject'><br><br>
Body:<br>
<textarea name='body' rows='6' cols='40'></textarea><br><br>
<input type='submit' value='Send'>
</form>
</body></html>
""")
def doPost(self, request, response):
to = request.getParameter("to")
subject = request.getParameter("subject")
body = request.getParameter("body")
props = Properties()
props_path = self.getServletContext().getRealPath("/WEB-INF/mail.properties")
props.load(FileInputStream(props_path))
session = Session.getInstance(props, None)
msg = MimeMessage(session)
msg.setFrom(InternetAddress(props.getProperty("mail.smtp.user")))
msg.setRecipient(Message.RecipientType.TO, InternetAddress(to))
msg.setSubject(subject)
msg.setText(body)
result = ""
try:
transport = session.getTransport("smtp")
transport.connect(props.getProperty("mail.smtp.host"), props.getProperty("mail.smtp.user"), props.getProperty("mail.smtp.pass"))
transport.sendMessage(msg, msg.getAllRecipients())
transport.close()
result = "OK"
except Exception as e:
result = "FAILURE: " + str(e)
response.setContentType("text/html;charset=UTF-8")
out = response.getWriter()
out.println("<html><body>")
out.println("<h2>Result: %s</h2>" % result)
out.println("<a href='SendMailServlet.py'>Back to form</a>")
out.println("</body></html>")
Below is a Jython servlet (DBTest.py) handling database insert + read. We assume a JDBC database such as MySQL or MariaDB is pre-created.
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(120),
subject VARCHAR(255),
body TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);And the jython code is:
# -*- coding: utf-8 -*-
from javax.servlet.http import HttpServlet
from java.sql import DriverManager
from java.lang import Class
from java.util import Properties
from java.io import FileInputStream
class DBTest(HttpServlet):
def init(self, config):
self.props = Properties()
props_path = config.getServletContext().getRealPath("/WEB-INF/db.properties")
self.props.load(FileInputStream(props_path))
def doPost(self, request, response):
email = request.getParameter("email")
subject = request.getParameter("subject")
body = request.getParameter("body")
try:
Class.forName("com.mysql.cj.jdbc.Driver")
conn = DriverManager.getConnection(self.props.getProperty("url"), self.props.getProperty("user"), self.props.getProperty("pass"))
stmt = conn.prepareStatement(
"INSERT INTO messages (email, subject, body) VALUES (?, ?, ?)"
)
stmt.setString(1, email)
stmt.setString(2, subject)
stmt.setString(3, body)
stmt.executeUpdate()
conn.close()
result = "Message saved successfully."
except Exception as e:
result = "DB error: " + str(e)
out = response.getWriter()
out.println("%s" % result)
def doGet(self, request, response):
try:
Class.forName("com.mysql.cj.jdbc.Driver")
conn = DriverManager.getConnection(self.props.getProperty("url"), self.props.getProperty("user"), self.props.getProperty("pass"))
stmt = conn.createStatement()
rs = stmt.executeQuery(
"SELECT id, email, subject, created_at FROM messages ORDER BY id DESC LIMIT 20"
)
out = response.getWriter()
out.println("<h1>Last 20 messages</h1>")
out.println("<ul>")
while rs.next():
out.println("<li>%s - %s - %s</li>" % (
rs.getInt("id"),
rs.getString("email"),
rs.getTimestamp("created_at")
))
out.println("</ul>")
conn.close()
except Exception as e:
out = response.getWriter()
out.println("DB error: " + str(e))
Here goes a simple Hello World (index.py) example:
# -*- coding: utf-8 -*-
# index.py – main form page for Jython webapp
from javax.servlet.http import HttpServlet
class index(HttpServlet):
def doGet(self, req, res):
res.setContentType("text/html");
out = res.getOutputStream()
print >>out, "<html>"
print >>out, "<head><title>Hello World, How are we?</title></head>"
print >>out, "<body>Hello World, how are we?"
print >>out, "</body>"
print >>out, "</html>"
out.close()
And finally, an API endpoint (api/status.py) example:
# -*- coding: utf-8 -*-
from javax.servlet.http import HttpServlet
from javax.servlet.http import HttpServletRequest, HttpServletResponse
import json
from java.lang import System
class status(HttpServlet):
def doGet(self, req, res):
data = {
"status": "ok",
"python_version": "Jython",
"timestamp": str(System.currentTimeMillis())
}
res.setContentType("application/json")
res.getWriter().write(json.dumps(data))
jython-examples
├── api
│ └── status.py
├── DBTest.py
├── index.py
├── SendMailServlet.py
└── WEB-INF
├── classes
├── db.properties
├── lib
│ ├── javax.activation.jar
│ ├── javax.mail.jar
│ ├── jython-standalone-2.7.4.jar
│ └── mysql-connector-j-8.0.33.jar
├── mail.properties
└── web.xml
WEB-INF/lib/Download:
jython-standalone.jar
Place in:
WEB-INF/lib/
The jar will be needed by all jython apps. For our specific apps please also download and place javax.activation.jar, javax.mail.jar and mysql-connector-j-8.0.33.jar in the WEB-INF/lib directory.
web.xml and set properties needed by 2 appsweb.xml should contain:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Jython App</display-name>
<servlet>
<servlet-name>JythonServlet</servlet-name>
<servlet-class>org.python.util.PyServlet</servlet-class>
<init-param>
<param-name>python.cachedir</param-name>
<param-value>WEB-INF/cache</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JythonServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.py</welcome-file>
</welcome-file-list>
</web-app>
mail.properties (for SendMailServlet.py) should contain (adjust the variables):
[email protected]
mail.smtp.pass=mail_pass
mail.smtp.host=localhost
mail.smtp.port=587
mail.smtp.auth=true
mail.smtp.starttls.enable=true
mail.smtp.ssl.protocols=TLSv1.1 TLSv1.2
db.properties (for DBTest.py) should contain (adjust the variables):
url=jdbc:mysql://localhost:3306/db_name
user=db_user
pass=db_pass
cd jython-examples
jar -cvf ../ROOT.war .
As you can see, compilation is not needed like in case of regular .java files.
Copy the WAR into:
cp ROOT.war $CATALINA_HOME/webapps/
Tomcat will unpack it automatically.
Visit:
https://jython.domain.com/
https://jython.domain.com/SendMailServlet
https://jython.domain.com/DBTest
https://jython.domain.com/api/status.py
to test the 4 apps.
Add data to the database using the DBTest.py app:
curl -X POST \
-d "[email protected]" \
-d "subject=Hello" \
-d "body=Testing JDBC" \
https://jython.domain.com/DBTest.py
Instead of manually zipping files, you can use Maven to automate WAR creation and dependency management.
Create the following pom.xml in the parent directory of jython-examples directory
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ROOT</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Jython Standalone -->
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.4</version>
</dependency>
<!-- Optional: MySQL JDBC -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.sun.activation</groupId>
<artifactId>javax.activation</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<finalName>ROOT</finalName>
<plugins>
<!-- WAR plugin -->
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<webResources>
<resource>
<directory>jython-examples</directory>
<targetPath>/</targetPath>
<includes>
<include>**/*</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
<!-- Copy WAR to Tomcat webapps -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<copy file="${project.build.directory}/${project.build.finalName}.war"
todir="${env.CATALINA_HOME}/webapps"/>
</target>
<target>
<delete dir="${project.build.directory}"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
From the parent directory run:
mvn clean install
This will produce the ROOT.war and copy it to $CATALINA_HOME/webapps for automatic deployment.
If you prefer Ant, here’s a simple build.xml to create and deploy the WAR. Place it in the parent directory of jython-examples.
<project name="Jython examples" default="deploy" basedir=".">
<property environment="env"/>
<property name="build.dir" value="build"/>
<property name="web.dir" value="jython-examples"/>
<property name="war.name" value="ROOT.war"/>
<property name="tomcat.webapps" value="${env.CATALINA_HOME}/webapps"/>
<target name="clean">
<delete dir="${build.dir}"/>
<delete file="${war.name}"/>
</target>
<target name="compile">
<mkdir dir="${build.dir}"/>
</target>
<target name="war" depends="compile">
<delete file="${war.name}"/> <!-- remove old WAR -->
<war destfile="${war.name}" webxml="${web.dir}/WEB-INF/web.xml">
<fileset dir="${web.dir}" includes="**/*"/>
</war>
</target>
<target name="deploy" depends="war">
<copy file="${war.name}" todir="${tomcat.webapps}"/>
<delete dir="${build.dir}"/><!-- remove build directory -->
</target>
</project>
Usage:
ant deploy
This will:
src/main/webappwebapps folderAdvantages of Ant vs Maven:
| Feature | Maven | Ant |
|---|---|---|
| Dependency Management | Automatic | Manual |
| WAR Packaging | Automatic | Manual via <war> task |
| Copy to Tomcat | Easy via plugin | Easy via <copy> task |
| Build Scripts | XML + conventions | XML only, flexible |
Above setups lets you automatically package and deploy the jython-examples web app using either Maven or Ant.
jython.domain.com or test the apps without using a subdomain.INFO: Initializing Jython runtime from jython-standalone-2.7.3.jarSee also Why Jython Hosting on Tomcat Is Different from CPython