docs/polyglot/java.md
This document deals with the implementation of polyglot interoperation with Java in the runtime. Please familiarise yourself with the general operation of polyglot bindings.
<!-- MarkdownTOC levels="2,3" autolink="true" --> <!-- /MarkdownTOC -->In order for the Enso runtime to effectively find Java objects for working with
in a polyglot fashion, it will look in the polyglot/java subdirectory of an
Enso project. This directory has the following requirements placed on it.
java directory should contain only .jar files and
directories..class files
at the appropriate points..jar files and directories are added to the runtime class-path for
Enso, and hence be made available to Enso programs.The actionables for this section are:
- In future, we want to expand this to support
.classfiles directly, and maybe even compiling Java code.
The dynamic polyglot system is a dynamic runtime lookup for Java objects, allowing Enso code to work with them through a runtime reflection-style mechanism. It is comprised of the following components:
Java.lookup_class : Text -> Any: A function that lets users look up a class
by a given name on the runtime classpath.An example can be found below:
from Standard.Base.Polyglot import Java, polyglot
main =
class = Java.lookup_class "org.enso.example.TestClass"
instance = class.new (x -> x * 2)
method = Polyglot.get_member instance "callFunctionAndIncrement"
Polyglot.execute method 10
The actionables for this section are:
- Expand on the detail when there is time.
Java can load native libraries using, e.g., the
System.loadLibrary
or
ClassLoader.findLibrary
methods. If a Java method loaded from the polyglot/java directory in project
Proj tries to load a native library via one of the aforementioned mechanisms,
the runtime system will look for the native library in the polyglot/lib
directory within the project Proj. The runtime system implements this by
overriding the
ClassLoader.findLibrary
method on the ClassLoader used to load the Java class.
The algorithm used to search for the native libraries within the polyglot/lib
directory hierarchy conforms to the
NetBeans JNI specification:
Lookup of library with name native works roughly in these steps:
libnative.so on Linux.polyglot/lib directory.polyglot/lib/<arch> directory, where <arch>
is the name of the architecture.polyglot/lib/<arch>/<os> directory, where
<os> is the name of the operating system.Supported names:
<os> are linux, macos, windows.
<arch> are amd64, x86_64, x86_32, aarch64.A typical use-case when bringing in some popular Java library into Enso ecosystem is to download it (including is transitive dependencies) from Maven Central - a popular place hosting thousands of Java libraries. Let's start from scratch by creating an empty Enso project:
$ bin/enso --new polydemo
$ cd polydemo
polydemo$ find .
.
./src
./src/Main.enso
./package.yaml
To populate the appropriate polyglot/java subdirectory, let's create following
two files - pom.xml and assembly.xml and put them into root of the project,
next to package.yaml file. The content of assembly.xml is:
<?xml version="1.0"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>polyglot</id>
<formats>
<format>dir</format>
</formats>
<baseDirectory>/</baseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<scope>runtime</scope>
<outputDirectory>/</outputDirectory>
<outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>
</dependencySet>
</dependencySets>
</assembly>
and let the content of the pom.xml be:
<?xml version="1.0"?>
<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.yourorg.yourproject</groupId>
<artifactId>download</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Download JARs for Your Project</name>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>download</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<outputDirectory>polyglot</outputDirectory>
<appendAssemblyId>false</appendAssemblyId>
<finalName>java</finalName>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<!-- find your favorite Java library at maven.org
and put the co-ordinates here
-->
<groupId>com.google.analytics</groupId>
<artifactId>google-analytics-data</artifactId>
<version>0.44.0</version>
</dependency>
</dependencies>
</project>
The files are instructing Maven - standard Java
build tool - to download
google-analytics-data library
library version 0.44.0 and all its dependencies into your polyglot/java
directory. Of course, feel free to find different library on
Maven central to download - edit pom.xml
appropriately. Once your files are ready execute:
polydemo$ ls *ml
assembly.xml package.yaml pom.xml
polyglot$ mvn -q package
polydemo$ ls polyglot/java/*.jar
...
the mvn command invokes
Maven which in turns downloads all the requested
library JAR files (52 of them in the case of google-analytics-data) into
polyglot/java directory. Now you are ready to use them.
There is a class com.google.analytics.data.v1alpha.AlphaAnalyticsDataClient
among the downloaded libraries, as such let's modify src/Main.enso to:
polyglot java import com.google.analytics.data.v1alpha.AlphaAnalyticsDataClient
main =
client = AlphaAnalyticsDataClient.create
client.close
run the project and voilá, the Java classes are available to your Enso sources.
The static system, however, lets us do much better in terms of user experience. Instead of having to dynamically look things up at runtime, we can instead do the following:
An example can be found below:
polyglot java import com.example.MyClass as MyClassJava
main =
x = MyClassJava.foo 1 2 3
inst = MyClassJava.new a b c
bar = inst.methodName x y
The actionables for this section are:
- Expand on the detail as the implementation becomes clear.