Interfacing CAL to Java Frameworks — Part 1

This article discusses the differences between CAL as a client of Java libraries and CAL modules as clients of Java frameworks.

CAL is a functional programming language which runs on the JVM. One of the advantages of a language which compiles to Java bytecode is that it is simple to call any of the many available Java libraries from CAL.

For example, here’s a simple example of a CAL function which creates a PDF document using the iText library:

createDoc :: String -> String -> ();
public createDoc fileName contents =
    let
        doc = newDocument;
    in
        getPdfWriterInstance doc (newFileOutputStream fileName) 
            `seq` 
        open doc 
            `seq` 
        add doc (newParagraph contents) 
            `seq` 
        close doc;

The function simply strings together imperative calls to Java. The java classes and methods required are imported thus:

data foreign unsafe import jvm public "com.lowagie.text.Document" 
    public Document;
foreign unsafe import jvm "constructor" 
    public newDocument :: Document;
foreign unsafe import jvm "method open"
    public open :: Document -> ();
foreign unsafe import jvm "method close"
    public close :: Document -> ();
foreign unsafe import jvm "method add"
    public add :: Document -> Element -> Boolean;

data foreign unsafe import jvm public "com.lowagie.text.Element" 
    public Element;
foreign unsafe import jvm "constructor com.lowagie.text.Paragraph" 
    public newParagraph :: String -> Element;

data foreign unsafe import jvm public "com.lowagie.text.pdf.PdfWriter" 
    public PdfWriter;
foreign unsafe import jvm "static method com.lowagie.text.pdf.PdfWriter.getInstance"
    public getPdfWriterInstance :: Document -> OutputStream -> PdfWriter;

data foreign unsafe import jvm public "java.io.OutputStream" 
    public OutputStream;
foreign unsafe import jvm "constructor java.io.FileOutputStream" 
    public newFileOutputStream :: String -> OutputStream;

Note that some constructors are declared as returning instances of the base class of the instances they actually return to void any need to cast. While the imports are relatively verbose they make the client function very clear, and bring the Java types and functions into the CAL type system.

The above shows that you can take a Java library and quickly create a CAL client, with effort proportional to the amount to the library you use. There is certainly scope to do a more thorough wrapping of the iText library to create an interface with a functional feel.

Interfacing to a framework is not as straightforward. The framework will often need to be provided with call-backs to your code, and it may be necessary to manage the life-cycle of objects which span many calls to CAL functions. When you use the Tapestry web application framework, for instance, you must create a class for each page of your application. Hibernate or OpenJPA expect to be able to perform byte code modification on classes which represent your persistent entities.

A CAL module is not mapped to a Java class by the CAL compiler, at least not in a way which makes it accessible to a Java client. Instead, CAL provides a simple API to call CAL functions from Java.

We can write a Java program to call our CAL PDF generator above:

public class PdfTest {
    public static void main(String[] args) throws CALExecutorException, GemCompilationException
    {
        CompilerMessageLogger messageLogger = new MessageLogger();
        BasicCALServices bcs = BasicCALServices.makeCompiled("myworkspace.cws", messageLogger);
        if (bcs == null)
            throw new RuntimeException(messageLogger.toString());
        EntryPointSpec entryPointSpec = EntryPointSpec.make(QualifiedName.make("TDavies.ITextTest", "createDoc"));
        bcs.runFunction(entryPointSpec, new Object[] {args[0], args[1]});
    }
}

In a future article I’ll discuss how to hook CAL code into Java frameworks.

Post a Comment

Your email is never shared. Required fields are marked *

*
*