2011-02-24: Using a Java Library from Fantom

Fantom provides JavaFFI for interacting with Java libraries. As an example, I will use the weka data mining library and show how to use the library to load in some data, construct and test a classifier.

NOTE

As of version 1.0.73, Fantom still relies on Java 8 as its run-time. JavaFFI will not be able to find the standard Java libraries if used with a newer version of Java.

Installing the Java Library

Download weka and locate the file 'weka.jar' - this is the library. Fantom uses a set of standard locations to search, when looking for a java class. The ideal place for the libraries is under the home folder for your Fantom installation: 'lib/java/ext/'.

Using the Java Library

The following script shows how to include some classes from the Java and Weka libraries, then create and use instances of classes from the Weka API.

// Example of using weka from Fantom

using [java]weka.classifiers.trees::J48
using [java]weka.core::Instances
using [java]java.io::FileReader

class Main
{
  public static Void main (Str[] args)
  {
    echo ("File to use: " + args[0])
    // read in the data
    data := Instances(FileReader(args[0]))
    // set the class label as the last attribute
    data.setClassIndex (data.numAttributes() - 1)
    // construct the decision tree
    tree := J48() { buildClassifier (data) }
    // display the tree and related information
    echo (tree)
    // classify and display class of each instance
    data.numInstances.times |Int i|
    {
      classn := tree.classifyInstance (data.instance(i))
      echo ("${data.instance(i)} is classed as ${classn}")
    }
  }
}

Running the Script

Simply run the script like a normal Fantom script. In this example I have used a data file from the Weka distribution. The Fantom runtime locates and uses the Weka library as needed:

  >  fan run-j48.fan weather.nominal.arff 
  File to use: weather.nominal.arff
  J48 pruned tree
  ------------------

  outlook = sunny
  |   humidity = high: no (3.0)
  |   humidity = normal: yes (2.0)
  outlook = overcast: yes (4.0)
  outlook = rainy
  |   windy = TRUE: no (2.0)
  |   windy = FALSE: yes (3.0)
  
  Number of Leaves  :  5
  
  Size of the tree :  8
  
  sunny,hot,high,FALSE,no is classed as 1.0
  sunny,hot,high,TRUE,no is classed as 1.0
  overcast,hot,high,FALSE,yes is classed as 0.0
  rainy,mild,high,FALSE,yes is classed as 0.0
  rainy,cool,normal,FALSE,yes is classed as 0.0
  rainy,cool,normal,TRUE,no is classed as 1.0
  overcast,cool,normal,TRUE,yes is classed as 0.0
  sunny,mild,high,FALSE,no is classed as 1.0
  sunny,cool,normal,FALSE,yes is classed as 0.0
  rainy,mild,normal,FALSE,yes is classed as 0.0
  sunny,mild,normal,TRUE,yes is classed as 0.0
  overcast,mild,high,TRUE,yes is classed as 0.0
  overcast,hot,normal,FALSE,yes is classed as 0.0
  rainy,mild,high,TRUE,no is classed as 1.0

Note: I have many jar files on my CLASSPATH, and these cause Fantom to run very slow, as it seems to try reading all the libraries looking for the ones it needs. Before running the above script, clear out the classpath using (on Linux):

> export CLASSPATH=.

Limitations

The JavaFFI still has some limitations, which are documented on the documentation page. The main one of these for me is the lack of support for multi-dimensional arrays. One workaround is to create a wrapper in Java which Fantom can use, and the wrapper will create and pass along the required Java datatype. With the wrapper in place, the procedure for working with your wrapped library is as above.


Page from Peter's Scrapbook, output from a VimWiki on 2024-01-29.