Automating Google Workspace (GWS) & more with Apps Script
What do you mean it needs to be Java based? How jython saved the day.
1. What do you mean it needs to be Java based? How Jython saved the day! Mark Rees Group CTO Century Software Holdings Berhad http://www.censof.com/ @hexdump42
2. Century Software (M) Sdn Bhd http://www.centurysoftware.com.my Develop & implement Financial Management Software Solutions Some of our applications use Python. Who?
3. From a tender document ” The application must be written in an industry standard enterprise programming framework. Please state which framework. ” The problem
12. Pros Runs most existing Python code without mods No GIL Access to Java Libraries Cons Performance No C extensions Pros & Cons
13. Call Java Code from com.google.i18n.phonenumbers import PhoneNumberUtil class PhoneNumberi18n: def __init__(self, country, phonenumber): self.phone_number_util = PhoneNumberUtil.getInstance() self.raw_phone_number = phonenumber self.country = country self.phone_number = self.phone_number_util.parse(phonenumber, country) def is_valid_number(self): return self.phone_number_util.isValidNumber(self.phone_number) def format_as_international(self): return self.phone_number_util.format(self.phone_number, PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL) Let's format some phone numbers http://code.google.com/p/libphonenumber/
14. Jython has a GUI from java.awt import BorderLayout from javax.swing import JButton, JFrame, JPanel, JLabel class SimpleGUI(object): def __init__(self): self.frame = JFrame( 'Hello World' , defaultCloseOperation = JFrame.EXIT_ON_CLOSE, size = ( 400, 400 )) self.label = JLabel ( 'Hello World' ) self.panel = JPanel() self.button = JButton( 'Click Me' , actionPerformed=self.hello) self.panel.add(self.button) self.content_panel = self.frame.getContentPane() self.content_panel.setLayout(BorderLayout()) self.content_panel.add(self.label, BorderLayout.CENTER) self.content_panel.add(self.panel, BorderLayout.PAGE_END) self.frame.visible = True def hello(self, event): self.label.setText( 'Hello user who clicked' ) if __name__ = = '__main__' : SimpleGUI()
15. Calling Jython from Java from com.censof.examples.interfaces import PersonType Class Person(Persontype) def __init__(self,name,gender): self.name = name self.gender = gender def getPersonName(self): return self.name Support for Java to use Jython classes It's not simple. Need jython class and interface. // Java interface for Person object package com.censof.examples.interfaces; public interface PersonType { public String getPersonName(); }
16. Calling Jython from Java package com.censof.examples.util; import com.censof.interfaces.PersonType; import org.python.core.PyObject; import org.python.core.PyString; import org.python.util.PythonInterpreter; public class PersonFactory { private PyObject personClass; PythonInterpreter interpreter = new PythonInterpreter(); interpreter.exec("from Person import Person"); personClass = interpreter.get("Person"); public PersonType create (String name, String location, String id) { PyObject personObject = personClass.__call__(new PyString(name), new PyString(gender), return (PersonType)buildingObject.__tojava__(PersonType.class); } } Then we need an object factory to coerce Jython module into a Java class
17. Calling Jython from Java package com.censof.examples; import com.censof.examples.util.PersonFactory; import com.censof.examples.interfaces.PersonType; public class Main { private static void print(PersonType person) { System.out.println("Person: " + person.getPersonName(); } public static void main(String[] args) { PersonFactory factory = new PersonFactory(); print(factory.create("Mark", "male")); print(factory.create("Joanne", "female")); } } Now we can call the jython class from java. Told you it's not simple.
18. Jython Scripting from Java import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class JSRMain { public static void main(String[] args) throws ScriptException { ScriptEngine engine = new ScriptEngineManager().getEngineByName("python"); engine.eval("import sys"); engine.eval("print sys.path"); engine.put("who", "Mark"); engine.eval("print who"); engine.eval("sum = 2 + 2"); Object x = engine.get("sum"); System.out.println("sum: " + x); } } JSR-223 enabled dynamic languages to be callable via Java.
19. Jython Scripting from Java import org.python.core.PyException; import org.python.core.PyInteger; import org.python.core.PyString; import org.python.core.PyObject; import org.python.util.PythonInterpreter; public class Main { public static void main(String[] args) throws PyException { PythonInterpreter interp = new PythonInterpreter(); interp.exec("import sys"); interp.exec("print sys.path"); interp.set("who", new PyString("Mark")); interp.exec("print who"); interp.exec("sum = 2+2"); PyObject x = interp.get("sum"); System.out.println("sum: " + x); } } You can also use the PythonInterpreter directly.
20. zxJDBC – Python DBAPI bridge to JDBC export CLASSPATH=/usr/share/java/postgresql.jar from __future__ import with_statement from com.ziclix.python.sql import zxJDBC jdbc_url = "jdbc:postgresql:wiki" username = "postgres" password = "secret" driver = "org.postgresql.Driver" with zxJDBC.connect(jdbc_url, username, password, driver) as conn: with conn: with conn.cursor() as c: c.execute( "select * from pages" ) c.fetchone() Database Access
27. pip install django Downloading/unpacking django Downloading Django-1.3.tar.gz (6.5Mb): 6.5Mb downloaded Running setup.py egg_info for package django Installing collected packages: django Running setup.py install for django Successfully installed django pip install django_jython==1.3.0b1 Downloading/unpacking django-jython==1.3.0b1 Downloading django-jython-1.3.0b1.tar.gz (42Kb): 42Kb downloaded Running setup.py egg_info for package django-jython Installing collected packages: django-jython Running setup.py install for django-jython Successfully installed django-jython In jython environment, install django: Then install django_jython Jython & the Web - django
28. django-admin.py startproject wiki cd wiki vi settings.py DATABASES = { 'default': { 'ENGINE': 'doj.backends.zxjdbc.postgresql', # 'doj.backends.zxjdbc. postgresql', 'doj.backends.zxjdbc.mysql', 'doj.backends.zxjdbc.sqlite3' or ‘ doj.backends.zxjdbc.oracle'. … . } export CLASSPATH=/usr/share/java/postgresql.jar:$CLASSPATH jython manage.py syncdb jython manage.py runserver Validating models... 0 errors found Django version 1.3, using settings 'wiki.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C. Then configure using zxjdbc for databases: Jython & the Web - django
29. vi settings.py # Add doj to INSTALLED_APPS INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', … . doj,) Thanks to modjy & django_jython we can deploy as a servlet: jython manage.py war –include-java-libs=/usr/share/java/postgresql.jar Copying WAR skeleton... Copying jython.jar... … . Copying postgresql.jar... Building WAR on /home/mark/swdev/jython-pycon-apac-2011/django_wiki/wiki.war... Finished. Now you can copy wiki.war to whatever location your application server wants it. Create WAR for deployment: Jython & the Web - django
31. Most things work of of the box under Jython Database access thanks to SQLAlchemy snakefight handles deployment under Java servlet containers. Works with any paster based web app. http://pypi.python.org/pypi/snakefight Jython & the Web - pyramid
Allowed us to use our existing python code with no or min code change
Allowed us to use our existing python code with no or min code change Haven't found that only Python 2.5 support an issue Works with virtualenv, easy_install, pip etc
Need Java JRE 5 or 6 installed Download jython installer jar from http://www.jython.org/downloads.html
Performance Startup time an issue but less impact for long running processes Replace Python code with calls to Java libs where speedup necessary
Needed to format phone numbers, looked for existing code. Found libphonenumber, Google's common Java library for parsing, formatting, storing and validating international phone numbers, at the time, no Python library (there is now :-]). Java code must be in CLASSPATH
Cpython has tcl/tk as it ’ s ” standard ” GUI. Does anyone use it about from IDLE Jython ’ s GUI is what ships with Java AWT/Swing. a It ’ s a nice GUI toolkit and Jython makes it esy to use.
jython.jar must be in CLASSPATH Also JSR-223 only works with jython 2.5.1+
Support prior to Jython 2.5.1 Allows access to Python Objects
zxJDBC was contributed by Brian Zimmer,a Jython committer. This API was written to enable Jython developers to have the capability of working with databases using techniques that more closely resembled the Python DB API. Originally a standaolne package it is now part of the Jython distribution
Thanks to zxjdbc, SQLAlchemy runs great under Jython. With exception of jdbc uri, the rest of the code is identical to CPython.
Jython script must be the same name as the class Create MyJythonServlet sub directory in Tomcat webapps directory. Copy web.xml to WEB-INF directory in the MyJythonServlet directory.
. Created by Alan Kennedy during early days of WSGI PEP 333.Now part of jython distribution.
django_jython Development server runs under jython
Support zxjdbc support added by django_jython package.
New command war added by django_jython means no need to create web.xml or package as war file manually. Gotcha: If there is already a jython.jar & cachedir in server lib, you will get a ” maximum recursion depth exceeded ” error. Two options: 1. Have separate jython.jars per app (default for doj) or 2. Use –shared-war doj war creation option.
Limitations – cannot use AST based template languages – no Chameleon, use Jinja2 or Mako Snakefight to package
Running inside WebSphere Using JasperReports Scripting workflow systems