ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 11.0

## 1.0 Introduction

The symbols in the jLinker module are exported from the package `:net.jlinker`. The package also has nickname `:javatools.jlinker` for back-compatibility with earlier versions. This nickname is deprecated and will be removed in a future release.

The purpose of this tool is to automate the interfacing of Lisp programs to Java class libraries. Jlinker supports a socket interface that allows the Lisp and Java parts of an application to run in separate process, and even in separate hosts. Jlinker also supports a native interface that allows the Lisp and Java parts of an application to share the address space of a single process. Both interfaces are accessed through the same API and in many cases, the choice of interface does not affect the application program style.

jLinker allows dynamic, unpremeditated access to the public methods, constructors, and members of Java classes from the Lisp runtime environment.

The end result is that the Lisp application may call Java methods as if they were Lisp functions. The documentation of the Java class is all that the Lisp programmer needs to know to use the Java library effectively. For example, the Java statements

        java.awt.Canvas canvas = new java.awt.Canvas();
        canvas.setSize( new java.awt.Dimension(12, 17) );

have the Lisp equivalent

        (setf canvas (jnew "java.awt.Canvas"))
        (jcall "setSize" canvas (jnew "java.awt.Dimension" 12 17))

Remote objects are retained as long as a reference exists in the calling environment. When a Lisp reference to a remote Java object is discarded and garbage collected, a reference to the Java object is eventually eliminated. The retention of the Java object is then controlled by Java rules.

To improve the efficiency of the interface, we allow values returned by methods to be ignored or copied. This prevents the construction of a remote object on the Java side. Ignored values are applicable to values that are already known or irrelevant to the Lisp application. Copied objects are applicable when the Java object is used only for the values of its fields and not for any method invocations.

Calls from Java to Lisp are implemented by the LispCall class in Java. To facilitate some commonly used callbacks from Java to Lisp we provide some adapter and listener classes that send AWT event data to a dispatcher on the Lisp side. This framework has been suitable for all the callbacks used in the java.awt library.

We have tested the concept with several Lisp applications that use the java.awt library for the gui component of the application. The performance of the gui is comparable to a native Java application in most cases. We have demonstrated portability by running the same application on Microsoft Windows NT and Sun Solaris.

Symbols naming Lisp operators and variables associated with jLinker are in the `net.jlinker` package. You may want to use this package so symbols need not be qualified with `net.jlinker`. Do this by evaluating `(use-package :net.jlinker)`. See use-package.

The jLinker module is loaded by evaluating:

    (require :jlinker)

You may load additional features (see Packaging Lisp applications as Java beans and servlets by evaluating:

    (require :jlinkent)

Note that only public methods, constructors, and members of public Java class may be accessed from Lisp. This constraint is implicit throughout this document whenever we mention a Java class, method, constructor, or member.

### 1.1 Jlinker and thread safety

The jLinker module is thread-safe in SMP or non-SMP Lisps.

## 2.0 Creating a Dynamically Linked Lisp/Java Application

### 2.1 Calling Java

Once a Jlinker interface has been initialized, Java constructors and methods are called by name.

All the following examples are shown in a `:case-sensitive-lower` Allegro CL lisp notation. In a standard (`:case-insensitive-upper` Allegro CL) all Java names would need to be enclosed in string quotes.

The form

       (jconstructor 'java.util.StringTokenizer
              'java.lang.String 'java.lang.String)

returns a reference to a constructor, and the form

       (jnew (jconstructor 'java.util.StringTokenizer
                     'java.lang.String 'java.lang.String)
              "ABC DEF GHI " " ")

returns a reference to an instance created by the constructor. These references are ordinary Lisp objects that may be bound to variables and stored in data structures.

       (jcall (jmethod 'java.util.StringTokenizer 'countTokens)
              x)

The operator lookup functions maintain a cache so that only the first mention of a class, constructor, method or field requires a remote call.

#### 2.1.1 Two Calling Models

We provide two calling models that may be used separately or simultaneously at the discretion of the programmer.

In the funcall model, Java classes and methods are referenced by name. This is a somewhat verbose style but is convenient for quick prototyping since all Java classes and methods are immediately available without any additional work on the Java or the Lisp side. For example, the following two statements

    (setq x (jnew (jconstructor "java.util.StringTokenizer"
                                "java.lang.String" "java.lang.String")
                  "a b c " " "))
    (jcall (jmethod "java.util.StringTokenizer" "nextToken") x)

create an instance of the Java `java.util.StringTokenizer` class and call the `nextToken` method on the the new instance. In the funcall model, a method or constructor may be specified with an incomplete signature such as

    (jconstructor "java.util.StringTokenizer" 2)

This notation specifies a constructor with two arguments. If there is only one constructor with two arguments in the Java class, then we return the constructor. Otherwise, we signal a continuable error where the desired constructor may be selected from a list of choices. A similar short-cut is possible in a call such as

    (jcall "nextToken" x)

Here we are calling the (only) `nextToken` method with zero arguments in the class of the object x. If several methods were available, then a continuable error would again be signalled.

Incomplete signatures are very convenient during development but should be avoided in final applications since searching for methods is a slow process that may require multiple round-trips between Lisp and Java.

In the class model, the user defines Lisp classes that correspond to Java classes and Lisp functions that correspond to Java constructors and Java methods. The Lisp functions that correspond to Java class methods are generic functions specialized on Lisp classes that correspond to Java classes. The Lisp functions that correspond to Java static methods are ordinary functions. To translate the preceding `java.util.StringTokenizer` example to the class model, we need to make some definitions:

    (def-java-class (tokenizer "java.util.StringTokenizer")
                    () () () ())
    (def-java-constructor tokenizer (tokenizer "java.lang.String"
                                               "java.lang.String"))
    (def-java-method (next-token "nextToken") (tokenizer))

When we use these definitions, the code is more compact and Lisp-like:

    (setq x (tokenizer "a b c " " "))
    (next-token x)

#### 2.1.2 Data Types and Conversions

When Lisp values are passed as arguments in a call to Java, the Lisp values are converted to the Java types specified in the method signature. The value returned by a call to Java is converted to a Lisp value following the rules in the table below.

In Java, values are automatically converted by calling the appropriate method of the LispCall class.

### Table of argument conversions from Lisp to Java

Declared Java Type | Allowed Lisp Type | Actual Java type (or value) | Conversion Note
boolean | null | (false) | v
boolean | any non-nil | (true) |
char | character | char |
byte | integer | byte | truncate to 7 bits + sign
short | integer | short | truncate to 15 bits + sign
int | integer | int | truncate to 31 bits + sign
long | integer | long | truncate to 63 bits + sign
float | number | float |
double | number | double |
java.lang.String | string | java.lang.String |
byte[ ] | sequence | byte[ ] | truncate to 7 bits + sign
short[ ] | sequence | short[ ] | truncate to 15 bits + sign
int[ ] | sequence | int[ ] | truncate to 31 bits + sign
float[ ] | sequence | float[ ] |
double[ ] | sequence | double[ ] |
String[ ] | sequence | String[ ] |
reference type | jwrapper | Java reference in wrapper |
reference type | null | (null) |
reference type | any | JLWrapper |

### Table of argument conversions from Java to Lisp

Java Type | Lisp Type (or Value)
boolean | (t or nil)
byte | integer
short | integer
int | integer
long | integer
char | character
String | string
float | double-float
double | double-float
byte[ ] | (array (signed-byte 8) (*))
short[ ] | (array (signed-byte 16) (*))
int[ ] | (array (signed-byte 32) (*))
float[ ] | (array double-float (*))
double | (array double-float (*))
String[ ] | (array t)
null | null
JLWrapper | Lisp type in wrapper
reference type | jwrapper

### COMPATIBILITY NOTE

The conversions in the above tables are new for Allegro CL 8.0. In earlier versions of Jlinker, the types in the method signature were not used during argument conversion and the function make-immediate-object was required to coerce many Lisp types to the correct Java type. Existing calls to make-immediate-object will still produce the correct result but in all cases these calls are now redundant.

#### 2.1.3 Two Implementations

There are two distinctly different implementations of the Jlinker interface.

In the socket implementation, the Lisp and Java parts of the application run in separate processes and may even run on separate hosts. A significant feature is that the address spaces of both parts are separate and protected. Each side of the application may be stopped and restarted without affecting the other. A disadvantage is that the speed of interactions is limited by the data transfer rate of sockets and may be subjected to indefinite network delays.

In the native implementation, the Lisp and Java parts of the application run in the same process and share the same address space. A significant feature is that calls between Lisp and Java run at the speed of foreign calls. A possible disadvantage may be that the address space may be reduced for both parts of the application. Anoter potential disadvantage is that a serious error in one part may cause the other part to crash as well.

### 2.2 Calling Style from Lisp to Java

The styles are funcall and class. The dynamic dispatch of generated lisp functions model is described in Dynamic Dispatch of Generated Lisp Functions.

#### 2.2.1 The Funcall Model

In this style, Java methods and constructors are referenced by specifying the full name class and signature of the intended method. This style is verbose, but convenient for occasional use of Java with no additional preparation.

We use the following meta notations for certain arguments:

- class-ref: an expression that evaluates to either (1) a string or symbol that names a Java class or (2) a remote reference object that points to a Java class object.
- method-ref: an expression that evaluates to either (1) a string or symbol that names a Java class or (2) a remote reference object that points to a Java method object.

In the cases where a compiler macro exists for a jLinker function, the compiler macro examines class-ref and method-ref arguments. If the arguments are compile-time constants which evaluate to a string or symbols, the compile-time values of these arguments are used to build the pre-load expressions.

- Jcall: Function
- jconstructor: Function
- jmethod: Function
- jstatic: Function

#### 2.2.2 The Class Model

In this style, Java methods and constructors are called by Lisp generic functions specialized on Lisp classes associated with Java classes. The Lisp functions and classes must be defined explicitly by the user.

The macro def-java-class can be used to define a Lisp class which corresponds to a Java class.

The macro def-java-constructor allows defining constructor functions to create instances of the classes defined with def-java-class. The macro def-java-method can be used to define methods. def-java-static defines static methods.

The function jclass-name-equal returns true if two argument strings name the same Java class.

#### 2.2.3 Dynamic Dispatch of Generated Lisp Functions

A new calling style was introduced in version 7.1.25 of Jlinker. Lisp functions are generated by analyzing Java jar files or classes. The names of the Lisp functions are systematically derived from the Java names and mimic the overloading style used in Java; in most cases, the Lisp name is easily deduced from the Java name so that perusing a translation table is rarely needed. The correct Java method to call is determined at run time by comparing the Lisp argument types to Java signatures.

In The Funcall Model, the Lisp program calls Java methods identified by their class, name and signature. In The Class Model, Java methods identified by their class name and signature, are associated with a Lisp function definition. In both cases, the programmer must identify by name and signature which Java implementation of a method is to be called. These two models enable a verbose and redundant style of programming and fail to take advantage of extensive static information available in the Java implementation.

This third access model implements a code generator that builds Lisp function definitions from the data available in the Java VM when a given library is loaded. The generated Lisp functions determine at run time which specific Java method must be called by matching the argument types against stored method signatures. The dispatch is done in Lisp and the only round-trip to Java is for the actual method call. The result is a much more compact and natural programming style that allows the Java library to be accessed much like a Lisp library.

The function scan-java-api generates a file of Lisp definitions. The function japi-reset may be needed if multiple Java libraries are used to generate separate Lisp packages. The function def-japi-classes defines named sets of Java classes. The variable *jdispatch-interactive* determines how some dispatch errors are handled.

### 2.3 Utility Lisp functions

- discard-in-java: Generic Function
- wrapped-object-p: Generic Function
- jarrayp: Function
- jarray-ref: Function: setf can be used with this function to set values in a Java array.
- jarray-set: Function: use of this function is deprecated. Use jarray-ref and setf instead.
- jclass: Function
- jclass-name: Function
- jclass-of: Function
- jdesc: Function
- jfield: Function
- jlinker-version: Function
- jlinker-buffer-size: Function
- jlookup: Generic Function
- jnew: Function
- jnew-array: Function
- kcons: Function
- kmeth: Function
- last-use: Generic Function
- make-dist-object: Generic Function
- make-immediate-object: Function
- eval-from-string: Function
- wrap-object: Generic Function

### 2.4 Dynamic Linkage Java Reference

All the following classes and methods are defined in Java package `com.franz.jlinker` and supplied in the file jlinker.jar.

jlinker requires Java Version 1.5 or later.

The recommended Java interface to jLinker is implemented in Java class LispCall. The class LispConnector is retained for back-compatibility but is now deprecated. Classes are documented in JavaDoc files supplied with the documentation (linked to just below). These two classes provide all the API that a Java program needs to call Lisp or to allow Lisp to call Java. The remainder of this section describes additional Java classes and methods that implement similar but deprecated interfaces retained for compatibility with older versions of ACL.

LispCall Java class documentation: the JavaDoc pages are automatically generated and do not have links to this documentation. Click here to open the LispCall documentation in a new window, and here to (usually) open in in this window.

LispConnector Java class documentation: the JavaDoc pages are automatically generated and do not have links to this documentation. Click here to open the LispConnector documentation in a new window, and here to (usually) open in in this window.

The JavaDoc index: click here to see the index of the available JavaDocs in a new browser window and here in this window (usually). Only the LispCall and LispConnector classes have JavaDoc documentation.

    Package: com.franz.jlinker
      Class: JavaLinkDist.JLinkerException

This is an abstract superclass of all the jLinker exceptions.

    Package: com.franz.jlinker
      Class: JavaLinkDist.InvokeException

This exception is thrown when some unexpected situation occurs in a call to invokeInLispEx. `getMessage()` returns a string of the form

    "Nothing returned from Lisp"
    "Unexpected value: ..."
    "Unexpected result: ..."

when such an exception is signaled.

    Package: com.franz.jlinker
      Class: JavaLinkDist.LispException

This exception is thrown when a Lisp error occurs in a call to invokeInLispEx. `getMessage()` returns a string description of the error when such an exception is signaled.

    Package: com.franz.jlinker
      Class: JavaLinkDist
     Method: lispError

        public static String lispError( JavaLinkDist.LispException x );

This method returns a remote reference to the Lisp error that caused the JavaLinkDist.LispException exception. `stringValue(err, 0)` returns a string containing the Lisp type of the error. `stringValue(err, 1)` returns a string containing the ~A representation of the Lisp error.

    Package: com.franz.jlinker
      Class: JavaLinkDist.LispThrow

This exception is thrown when a Lisp throw terminated the Lisp call initiated by a call to invokeInLispEx.

### 2.5 Initialization Functions and Variables

The functions described in this section are used to setup and query the interface between Lisp and Java.

The functions and variables are:

- jlinker-init: This function establishes communication with a Java server.
- *jlinker-init*: this variable specifies a default set of arguments to the jlinker-init function. The initial value in the jlinker module is `(:start-java)`.
- jlinker-query: This function sends a query to the jLinker interface.
- jlinker-end: End a jLinker connection.
- jlinker-listen: This function starts a process that continuously restarts the Lisp jLinker server so that one is (almost) always available for a Java connection.
- jlinker-slot: The purpose of this function is to extract and modify data specific to one particular Lisp-Java connection.
- *jlinker-connection*: This anchor variable defines all the parameters for one complete connection between Lisp and Java.
- *jlinker-verbose*: The value of this variable is the default value for the :verbose keyword argument to jlinker-init and jlinker-end. When true Lisp emits many status messages while connecting.
- *jlinker-debug*: The value of this variable is the default value for the :debug keyword argument to jlinker-init.
- *jlinker-error-p*: The value of this variable is the default value for the :error-p keyword argument to jlinker-init and jlinker-end.
- *jlinker-set-accessible*: The value of this special variable determines the jLinker behavior when Java throws `java.lang.IllegalAccessException` during a method call.
- *jlinker-run-java*:
- *file-type-comparator*: The value of this variable should be a function name or function object of the function used to compare file types.
- *jlinker-deprecated-warnings*: if this variable is set to a non-`nil` value, then the compiler will signal warnings if deprecated operators are encountered. In any case, the compiler collects the operators of deprecated forms in a list stored in this variable. The initial value is `nil`.
- jstart-connection-pool: Initialize a connection pool when the jlinker-init call specified delayed connection pool start.
- jquery-connection-pool: Query the state of the jlinker connection pool.
- *jlinker-default-connection-pool*: Specify the default connection pool parameters.
- jpool-statistics: Query connection pool usage statistics.
- jpool-slot: Query values from the connection pool state.
- When jlinker-init starts the Java VM, wait this long for the VM to respond. The default is 10 seconds.
- *jlinker-reply-timeout*: The timeout for a single application call to Java. The default is 30 seconds.
- *jlinker-port-timeout*: The timeout for acquiring a port to Java. The default is -1 to denote *jlinker-reply-timeout*.
- *jlinker-request-timeout*: Wait this many seconds for a call from Java. The default is `nil` to wait indefinitely.

On MS Windows, jLinker is able to locate the Java executable and the required jar files by examining the Windows registry. Therefore, in most installations, it is not necessary to configure jLinker.

On Unix, Linux or MacOSX systems, we have not discovered a general method for discovering the location of the Java executable and libraries. Therefore some configuration is necessary. The sample file jlinker/jl-config.cl is a template that can be customized to set the jLinker configuration variables in several different situations.

- *jlinker-java-home*: this variable must be set to the location of the Java installation. On MS Windows, the keyword `:find` specifies a Registry search.
- *jni-library*: this variable is used only when the native Java interface is used. The value of this variable must be the pathname string of the Java JNI shared library (libjni.so, libjni.dll, libjvm.dylib ...). If the value is a relative path string, then it is merged with the value of *jlinker-java-home*. On MS Windows, the keyword `:find` specifies a Registry search.

### 2.6 Event Handling

Many Java classes customize their behavior by allowing the programmer to extend them with custom implementations of selected methods. The java.awt package makes extensive use of this facility to handle the events associated with the use of a GUI.

In a distributed computing environment, the question arises of where the custom implementations of the methods should be executed. There is a range of answers to this question and some of the possibilities are discussed in the following sections.

If the custom behavior of an extended method does not require any data from the Lisp side of the application, the method can be implemented in a pure Java extension of the class in question. The extended method may be linked to the application from Lisp.

The Java code:

    public class MyWindowAdapter extends WindowAdapter {

      public void windowClosing(WindowEvent e) {
        e.getWindow().dispose();
      };

The Lisp code:

    (jcall "addWindowListener" frame (jnew "MyWindowAdapter"))

The Java method may also call back to Lisp with `LispCall` methods.

#### 2.6.1 Lightweight Callback to Lisp Methods

When callback methods follow a common pattern, it may be possible to implement a general function that passes enough information from Java to Lisp through a common interface.

In the case of java.awt events, this is a very reasonable approach, and we have subclassed many of the event handlers to transmit event information to Lisp in a common form where it is dispatched by Lisp functions.

    (jcall (jmethod "com.franz.jlinker.JLWindowAdapter" "addTo") frame)
    (jregister-handler frame :windowClosing #'(lambda (data frame &rest x)
                                                (jcall "dispose" frame)))

This approach can be extended or modified to handle a wide range of callback situations.

#### 2.6.2 Lisp Functions to Dispatch Java Events

- jnotify-lisp
- jregister-handler
- jquery-handler

#### 2.6.3 Implemented Sub-Classes of AWT Event Handlers

### ActionListener

       class com.franz.jlinker.JLActionListener implements ActionListener

       Methods:
         addTo(java.awt.Button)
         addTo(java.awt.List)
         addTo(java.awt.MenuItem)
         addTo(java.awt.TextField)

       Handler arguments:
         object is argument to addTo
         event=   :actionPerformed
         longs=   { event.getModifiers() }
         strings= { event.paramString(), event.getActionCommand() }

### ComponentAdapter

       class com.franz.jlinker.JLComponentAdapter extends ComponentAdapter

       Methods:
         addTo(java.awt.Component)

       Handler arguments:
         object is argument to addTo
         event=   :componentResized   :componentMoved
                  :componentShown     :componentHidden
         longs=   { }
         strings= { event.paramString() }

### ItemListener

       class com.franz.jlinker.JLItemListener implements ItemListener

       Methods:
         addTo(java.awt.Checkbox)
         addTo(java.awt.CheckboxMenuItem)
         addTo(java.awt.Choice)
         addTo(java.awt.ItemSelectable)
         addTo(java.awt.List)

       Handler arguments:
         object is argument to addTo
         event=   :itemStateChanged
         longs=   { (event.getStateChange()==event.SELECTED)?1:0 }
         strings= { event.paramString(), (event.getItem()).toString() }

### KeyAdapter

       class com.franz.jlinker.JLKeyAdapter extends KeyAdapter

       Methods:
         addTo(java.awt.Component)

       Handler arguments:
         object is argument to addTo
         event=   :keyTyped   :keyPressed   :keyReleased
         longs=   { event.getModifiers(), (event.isActionKey()?1:0),
                    event.getKeyCode() }
         strings= { event.paramString() }

### MouseAdapter

       class com.franz.jlinker.JLMouseAdapter extends MouseAdapter

       Methods:
         addTo(java.awt.Component)

       Handler arguments:
         object is argument to addTo
         event= :mouseClicked  :mousePressed  :mouseReleased
                               :mouseEntered  :mouseExited
         longs=   { event.getModifiers(), (event.isPopupTrigger()?1:0),
                    event.getClickCount(), event.getX(), event.getY() }
         strings= { event.paramString() }

### MouseMotionAdapter

       class com.franz.jlinker.JLMouseMotionAdapter extends MouseMotionAdapter

       Methods:
         addTo(java.awt.Component)

       Handler arguments:
         object is argument to addTo
         event= :mouseDragged   :mouseMoved
         longs=   { event.getModifiers(), (event.isPopupTrigger()?1:0),
                    event.getClickCount(), event.getX(), event.getY() }
         strings= { event.paramString() }

### WindowAdapter

       class com.franz.jlinker.JLWindowAdapter extends WindowAdapter

       Methods:
         addTo(java.awt.Window)

       Handler arguments:
         object is argument to addTo
         event=   :windowOpened  :windowClosing    :windowClosed
                                 :windowIconified  :windowDeiconified
                             :windowActivated  :windowDeactivated
         longs=   { }
         strings= { }

### Generic Event Handler

The following code examples show parts of some of the above adapter implementations. The examples illustrate how to add a new event handler that propagates the Java event to the Lisp jregister-handler interface.

When the Java object supplied with the event is also the object registered in Lisp:

    package com.franz.jlinker;
    import java.awt.*;
    import java.awt.event.*;

    public class JLKeyAdapter extends KeyAdapter {

      // One addTo method is needed for each argument type.
      public static synchronized void addTo( Component comp ) {
        comp.addKeyListener( (KeyListener)(new JLKeyAdapter()) );
      }

      // One event method is needed for each event defined in the
      // listener or adapter interface.
      public void keyTyped(KeyEvent e) {
        String s = { e.paramString() };
        int[] l = { e.getModifiers(), (e.isActionKey()?1:0), e.getKeyCode() };
        LispCall.dispatchEvent("keyTyped", (Object)(e.getComponent()), s, l);
      }
    }

When the Java object associated with the event is not the object registered in Lisp:

    package com.franz.jlinker;
    import java.awt.*;
    import java.awt.event.*;

    public class JLActionListener implements ActionListener {

      private Object handle;

      // One addTo method is needed for each argument type.
      public static synchronized void addTo( Button comp ) {
        JLActionListener l = new JLActionListener();
        l.handle = (Object)comp;
        comp.addActionListener( (ActionListener)l );
      }

      // One event method is needed for each event defined in the
      // listener or adapter interface.
      public void actionPerformed(ActionEvent e) {

        String[] s = { e.paramString(), e.getActionCommand() };
        int[

    ]    l = { e.getModifiers() };

        LispCall.dispatchEvent("actionPerformed", handle, s, l

    );
      }

    }

### 2.7 I18N Issues

Characters are converted to 16-bit positive integers for transmission and then converted back to characters using the following primitive sequences.
This should yield consistent results when client and host are on the same machine.

                      Lisp                     Java

    Start String      s                        s
    Extraction        c<-(char s i)            c<-s.charAt(i)
    Conversion        n<-(char-code c)         n<-(int)c
    Transmit          16-bit n                 16-bit n
    Conversion        c<-(code-char n)         c<-(char)n
    Construction      s<-(make-string len)     sb<-new StringBuffer(len)
                  (setf (char s i) c)      sb.append(c)
                                           s <-sb.toString()
    Result String     s                        s

### 2.8 Java Applets

When calling Lisp from a Java Applet, the normal mode is to advertise in Lisp and use connect() in Java.

NOTE: The behavior of the plain APPLET tag in Netscape is not reliable. The plug-in style of applet activation seems to work without problems. In Netscape this is invoked with the EMBED html tag; in Internet Explorer, the OBJECThtml tag.

When a jLinker application is running as an applet in a browser, the security measures in the browser prevent the use of run-time method lookup in the Lisp application. All methods and constructors must be named with a complete signature in the Lisp code.

### 2.9 Re-entrancy, parallelism and connection pooling

In socket mode, one Java VM can connect to exactly one Lisp process. One Lisp OS process can connect to any number of Java VMs. Each connection is identified by a distinct binding of *jlinker-connection*.
Each call from Lisp to Java ties up one port (socket) from Lisp to Java for the duration of the call. Each call from Java to Lisp ties up one port form Java to Lisp.

If connection pooling is not enabled, there is exactly one port from Lisp to Java and one port from Java to Lisp. Consequently, a call from Lisp to Java can call back into Lisp, or a call from Java to Lisp can call back into Java, but any attemt at deeper nesting will signal a timeout error. Parallel calls from Lisp to Java, or from Java to Lisp are impossible.

When connection pooling is enabled, the depth of nesting or the level of parallelism is limited by the number of ports in the pool.

The functions that control connection pooling are:

- jlinker-init: The connection-pool argument specifies the connection pool parameters.
- jstart-connection-pool: This function is used to start the connection pool when delayed start was specifed in jlinker-init.
- jquery-connection-pool: The connection pool is normally initialized in parallel with the running jlinker application. This function is used to query the state of the pool. It can also be used to wait until the pool meets some criteria.
- jpool-slot: Accessor for current pool parameters.
- jpool-statistics: Accessor for current pool statistics.

In the native (JNI) implementation of jlinker, there are no recursion restrictions on calls between Lisp and Java. There is a thread restriction in ACL impementations that do not use OS threads (at this time all Unix ports): Java may call Lisp only in the thread in which Lisp initially started the Java VM. Any Lisp LWP may call Java since from the Java perspective all Lisp LWPs are the same thread.

### 2.10 Calling Methods of Inner Classes

JLinker uses Java Reflection methods to make all the method calls requested by the Lisp application. When an application attempts to call a method of an inner class as in the example below:

    (let* ((al (jnew "java.util.ArrayList"))
           (it (jcall "iterator" al)))
       (jcall "hasNext" it))

Java throws `java.lang.IllegalAccessException`.

Our experience shows that the accessibility of inner class methods is tested when Java reflection methods are used on them and the default accessibility of all methods is False. If the special variable *jlinker-set-accessible* is set to a non-`nil` value, then jLinker will automatically re-try the call after changing the accessibility of the method to True.

The application programmer can avoid the overhead of a double call by evaluating a form such as

    (jcall "setAccessible" m (make-immediate-object t :boolean))

for any methods known to be implemented in inner classes.

Naturally, if Java security settings prevent access to the accessibility setting of the method, then the method simply cannot be called from Lisp. One workaround in this case is to add a Java class that implements the desired method call from Java:

    public class Wrap {
      public static boolean hasNext( java.util.Iterator x ) {
        return x.hasNext();
      }
    }

The Lisp code for the previous example is then:

    (let* ((al (jnew "java.util.ArrayList"))
           (it (jcall "iterator" al)))
       (jstatic "hasNext" "Wrap" it))

A single wrapper class can be used to define any number of these helper methods.

### 2.11 Portability Issues

Lisp applications can interface to Java through both jlinker implementations with the same code. The only place where the jlinker implementation is apparent is in the call to jlinker-init. This part of the application can be made more portable with a suitable binding for *jlinker-init*.

Java applications that use only `LispCall` can interface to Lisp through both jlinker implementations with the same code.

Any part of the Java application that depends on the `LispConnector`, `JavaLinkDist`, and `TransStruct` classes can only be used with the socket implementation.

There is one important difference in the jlinker behavior that depends on the threads implementation of the Lisp image. In a Lisp implementation that uses native OS threads, Java and Lisp threads may call back and forth freely. In a Lisp implementation that does not use native OS threads, a Java application may call Lisp only from the one thread in which Lisp is running. Thus a call from Java to Lisp is possible only when Java is running in call from Lisp.

To overcome this limitation in graphic applications, the jlinker adapter and listener classes may be used to handle AWT events. These adapters and listeners queue Java events on the Java side. A Lisp process periodically polls this queue and dispatches the events to the Lisp handlers.

The mayCall() method in the `LispCall` class returns an integer that describes the thread callback restrictions:

- 0 - cannot call yet, interface is not fully initialized
- -1 - cannot call from this thread, ever
- +1 - may call from this thread, but not from any other
- +2 - may call from this or any other thread

### 2.12 Passing Array Arguments to Java

The class names that appear in Jlinker forms must be the strings that Java would return from getName of getClass for the corresponding object.

For a one-dimensional array object, the class name is a string of the form "[ttt" where `ttt` is a representation of the type or class of the element as follows:

         type: byte  char  double  float  int  long  short  boolean
          ttt:  B     C      D       F     I    J      S       Z

For arrays of objects, ttt is a string of the form "Lclassname;". Thus an array of String objects would have the class name "Ljava.lang.String;".

The name of 2-dimensional array begins with "[[", and so on.

A Lisp array that contains only integers in the Java int range, or only floats, or only strings, is transmitted to Java (by default) as a Java array of `int`, `double`, or `String`. Similarly, a Java array on `int`, `float`, `double`, or `String` is copied to Lisp (by default) as a Lisp array of Lisp values.

More complex arrays must be created in the home environment and transmitted by reference. To create a Java array from Lisp, use the jnew-array function. To create a Lisp array from Java, the Java code must call a Lisp function that creates an array.

## 3.0 Installation

### 3.1 Files involved in using jLinker

On the Lisp side, jlinker is enabled by evaluating the expression `(require :jlinker)` to load the jlinker.fasl file from the Allegro CL installation directory.

On the Java side, the file jlinker.jar must be visible in the classpath of the Java VM. This file is included in the jlinker sub-directory of the Allegro CL instllation.

When Lisp starts the Java VM, it needs to know the location of the Java run time files. On Windows, the files can be located in the Windows Registry. On Unix systems, we try several commonly used locations. If the Java files are not in a default location, they must be specified in the variable *jlinker-java-home*.

The file jl-config.cl is included in the jlinker distribution as an example of ways to specify the Java location when the default search is not appropriate.

### 3.2 One Lisp and Several Java Client/Server connections

The function jlinker-listen sets up a process that creates a new listener every time Java makes a new connection, so that it is always possible for Java to connect to Lisp, except for a narrow time slot when Lisp is processing a new connection. In this case, the style is always for Lisp to advertise and Java to connect.

When multiple connections are active, the code for each must run in a separate Lisp process, and in the scope of a separate binding of *jlinker-connection*.

### 3.3 Native Java Issues

This section covers some issues that apply only to the native jlinker implementation.

On some UNIX or Linux versions, it may be necessary to modify the environment variable LD_LIBRARY_PATH (DYLD_LIBRARY_PATH on macOS) to include the directory where the Java VM shared library (libjvm.so or libjvm.dylib) is located. This setting is required if the Lisp/Java application exits with the message

    Error occurred during initialization of VM
    Unable to load native library: libjvm.so: cannot open shared object file:

#### 3.3.1 LD_LIBRARY_PATH

If the environment variable LD_LIBRARY_PATH (DYLD_LIBRARY_PATH on macOS) is needed by the Java VM, the value must be set before Allegro CL is started. Calling (setf sys:getenv) (after Lisp has started) is not sufficient in this case. If the Lisp variable *jni-ld-path-p* is set to a non-`nil` value we search the locations specified in LD_LIBRARY_PATH (DYLD_LIBRARY_PATH on macOS) for a file named libjni.so, libjni.dll, libjvm.dylib, or if the value of *jni-ld-path-p* is a string, then a file with that name. If the file is not found, we signal a continuable error.

#### 3.3.2 Dumplisp

### Warning: jLinked connections cannot reliably be re-established in dumped images

If an Allegro CL image running jlinker in native mode is saved with dumplisp and re-started, the jlinker connection cannot be re-established reliably in the current implementation (jLinker 7.1.12 and later).

#### 3.3.3 Event Polling

In the Unix versions of Allegro CL we do not use native OS thread implementations. Consequently, Java methods can only call Lisp when called from Lisp initially. To allow GUI event callbacks to function, the listener and adapter classes described above can be used to queue AWT events in Java. The queue is polled periodically from Lisp and the events transferred to a Lisp scheduler. The following calls may be used to manage the poll behavior:

    (jlinker-slot :max-interval [new-value])
       The longest interval (in seconds) between polls.
       The initial value is 0.5.
    (jlinker-slot :min-interval [new-value])
       The shortest interval (in seconds) between polls.
       The initial value 0.075.
    (jlinker-slot :event-group [new-value])
       The maximum number of events to dequeue at each poll.
       The initial value is 5.

#### 3.3.4 Miscellaneous Caveats

If the Lisp application call the AWT "dispose" method for an object where "isDisplayable" is "false", the Java VM will not return to Lisp and the entire application will hang in a non-interruptible state. We recommend a form such as

    (when (jcall "isDisplayable" x) (jcall "dispose" x))

## 4.0 A Complete Code Example

All the following classes and methods are defined in Java package `com.franz.jlinker` and supplied in the file jlinker.jar.

We include here a complete example of a simple program.

    (in-package :user)

    ;;(set-case-mode :case-sensitive-lower)

    (require :jlinker)

    (use-package :net.jlinker)
    (defpackage :net.jlinker (:nicknames :jl))

    ;; Make sure the required files are locally visible
    ;; customized copy of [Allegro directory]/jlinker/jl-config.cl
    ;;                    [Allegro directory]/jlinker/jlinker.jar

    (load "jl-config")

    (defun new-tokenizer (&optional (string "A B C D ")
                    (delimiters " "))
      (jnew (jconstructor "java.util.StringTokenizer"
               "java.lang.String" "java.lang.String")
        string delimiters))

    (defun next-token (inst)
      (jcall (jmethod "java.util.StringTokenizer" "nextToken")
               inst))

    (defun run-tokenizer (&optional (string "A B C D ")
                    (delimiters " "))

      (or (jlinker-query) (jlinker-init))

      (let ((inst (new-tokenizer string delimiters))
        res)

        (dotimes (i (jcall (jmethod "java.util.StringTokenizer" "countTokens")
                   inst))
          (push (next-token inst)
            res))

        (values inst (reverse res))))

    ------------------- console log: ---------------------
    cl-user(4): :ld example
    ; Loading C:\mmWork\java\fi\java-cur\example.cl
    ;   Loading C:\mmWork\java\fi\java-cur\jl-config.cl
    cl-user(5): (run-tokenizer)
    ; Fast loading from bundle code\acldns.fasl.
    #<tran-struct Java IP 1004,118185548 java.util.StringTokenizer>
    ("A" "B" "C" "D")

        ;; the following example shows how a Java error
        ;; is mapped to a Lisp error

    cl-user(6): (next-token *)
    Error: Java error: java.util.NoSuchElementException
    result= "java.util.NoSuchElementException"

    Restart actions (select using :continue):
     0: Supply another value.
     1: Return to Top Level (an "abort" restart)
     2: Abort #<process Initial Lisp Listener(6d8)>
    [1c] cl-user(7): :pop
    cl-user(8):

There are additional code examples in */examples/jlinker/**, including:

    applet/      examples of Java applets connected to Lisp.
    javabean/    examples of Java Beans connected to Lisp.
    servlet/     examples of Java servlets connected to Lisp.
    timecard/    a complete Lisp application using the Java
             AWT classes for the user interface.

## 5.0 Packaging Lisp applications as Java beans and servlets

The jLinker Java Bean API facilitates the creation of Java Bean classes that call Allegro CL functions to do the work of the Java Bean. The jLinker Servlet API facilitates the creation of Java Servlets that call Allegro CL functions to do the work of the Servlet.

The extensions are loaded with the forms

    (require :jlinker)    ;; Available to all customers.
                          ;; returns NIL if jlinker is already
                          ;; loaded. jlinker module must be
                          ;; loaded before jlinkent module.
    (require :jlinkent)

jLinker includes support for Java Servlets and Java Beans. The Java support consists of Java classes that implement communication between a Java HttpServlet and a Lisp image. The Lisp support consists of classes and functions that implement the Lisp side of the interface. We also include examples of simple servlets where the work of the servlet is performed in Lisp.

### 5.1 The jLinker Java Bean API

The jLinker Java Bean API facilitates the creation of Java Bean classes that call Allegro CL functions to do the work of the Java Bean.

All Lisp symbols are in the package `net.jlinker`.

The example code in examples/jlinker/javabean is described in the file readme.txt.

#### Lisp API

See def-java-to-lisp-wrapper,

gen-output-lang, and gen-java-stream.

### 5.2 The jLinker Servlet API

The jLinker Servlet API facilitates the creation of Java Servlets that call Allegro CL functions to do the work of the Servlet.

All Lisp symbols are in the package `net.jlinker`.

Java signatures are taken from "Java Servlet API Specification - Version 2.1a - November 1998" from Sun Microsystems at http://java.sun.com/products/servlet/.

The example code in examples/jlinker/servlet is described in the file readme.txt.

### Lisp API

The `net.jlinker::servlet` class is the superclass of all the Lisp implementation classes that support the servlet interface. It has slots:

- `instances`: This is a class slot that keeps track of the number of instances of the class.
- `instance`: An instance slot that identifies each instance uniquely.
- `java-classes`: A list of strings naming Java classes that may invoke this Lisp class. Access from other Java classes causes an error signal.

The following functions and methods are defined:

- new-servlet, Arguments: lisp-class-name servlet-ref config-ref. This function is called from the Java servlet `init()` method. The lisp-class-name argument must be a string containing a fully qualified class name and the class must be a sub-class of http-servlet.
- destroy-servlet, Arguments: `(self net.jlinker::servlet)`. This method is called from the Java servlet `destroy()` method. The pre-defined primary method discards any locally cached information and any remote references.
- get-servlet-config, Arguments: `(self net.jlinker::servlet)`. Returns the ServletConfig reference saved from the call to `init()`.
- get-servlet-info, Arguments: `(self net.jlinker::servlet)`. Retrieve from Java a reference to the `ServletInfo` object.

The http-servlet class is a subclass of `net.jlinker::servlet`. This is the Lisp counterpart to the Java class `LispHttpServlet`.

This class implements dummy methods for all the Java methods in the Java class `HttpServlet`. User code should subclass this class and override any method definitions that are actually used by the application. The subclass must also define a value for `java-classes slot`.

The predefined dummy methods are:

- do-delete: Arguments: (self http-servlet) request response
- do-get: Arguments: (self http-servlet) request response
- do-head: Arguments: (self http-servlet) request response
- do-options: Arguments: (self http-servlet) request response
- do-post: Arguments: (self http-servlet) request response
- do-put: Arguments: (self http-servlet) request response
- do-trace: Arguments: (self http-servlet) request response

These are classes that should be subclassed by the application. The subclass defines working methods for the above generic functions. The subclass also defines a value for the `java-classes` slot:

- async-http-servlet
- multi-async-http-servlet

Two start-work methods are defined on instances of those classes. The argument lists are

> (self async-http-servlet) work request response gate

and

> (self multi-async-http-servlet) work request response gate.

### Java API

### Methods implemented in Java class LispHttpServlet.

    public void init(ServletConfig config)          Java Method

> The Java method invokes the Lisp function new-servlet to propagate this method call.

    public void service(...)                Java Method

> Handled by the Java super-class implementation.

    public void destroy()                   Java Method

> The Java method calls the Lisp destroy method.

    protected void doDelete(HttpServletRequest request, Java Method
                            HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-delete method.

    protected void doGet(HttpServletRequest request,    Java Method
                         HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-get method.

    protected void doHead(HttpServletRequest request,   Java Method
                          HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-head method.

    protected void doOptions(HttpServletRequest request,    Java Method
                             HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-options method.

    protected void doPost(HttpServletRequest request,   Java Method
                          HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-post method.

    protected void doPut(HttpServletRequest request,    Java Method
                         HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-put method.

    protected void doTrace(HttpServletRequest request,  Java Method
                           HttpServletResponse response)
              throws ServletException;

> The Java method calls the Lisp do-trace method.

### Methods implemented in Java class com.franz.jlinker.JavaLinkCommon.

    public static Object[

    ] newGate(

    )           Java Method

> Return a new closed gate.

    public static void testGate(Object[

    ] gate

    )       Java Method

> Wait for gate to open and return a String x.

        x.length()=0 if operation completed
        x.length()>0 if error or failure, string contains message

    public static Object[

    ] lispValues            Java Method
                  (res, called, min, max, firstRefP

    )

> Utility function called by the sample implementations of LispHttpServlet and LispAsyncHttpServlet to decode the result array returned from a call to Lisp.

        res    - result array returned from Lisp
        called - the name of the Lisp function called
        min    - the minimum number of expected values
        max    - the maximum number of expected values
        firstRefP - first returned value should be a remote reference
                to a Lisp object

> returned value is an array Object[2] where the first element is an Integer return code and the second element a String error message.

## 6.0 Index of classes, operators, and variables

- async-http-servlet Class
- def-japi-classes Function
- def-java-class Macro
- def-java-constructor Macro
- def-java-method Macro
- def-java-static Macro
- def-java-to-lisp-wrapper Macro
- destroy-servlet Generic Function
- discard-in-java Generic Function
- dist-object-p Function
- do-delete Generic Function
- do-get Generic Function
- do-head Generic Function
- do-options Generic Function
- do-post Generic Function
- do-put Generic Function
- do-trace Generic Function
- eval-from-string Function
- *file-type-comparator* Variable
- gen-java-stream Function
- gen-output-lang Function
- get-servlet-config Generic Function
- get-servlet-info Generic Function
- http-servlet Class
- japi-reset Function
- jarray-ref Generic Function
- jarray-set Generic Function
- jarrayp Generic Function
- jcall Function
- jclass Function
- jclass-name Function
- jclass-name-equal Function
- jclass-of Function
- jclass-paths Function
- jcons Function
- jconstructor Function
- jdesc Function
- *jdispatch-interactive* Variable
- jdo-call Function
- jdo-static Function
- jfield Function
- jget-class Function
- jget-cons Function
- jget-field Function
- jget-meth Function
- jget-properties Function
- jget-property Function
- jlinker-buffer-size Function
- *jlinker-connection* Variable
- jlinker-copy-base Function
- *jlinker-debug* Variable
- *jlinker-default-connection-pool* Variable
- *jlinker-deprecated-warnings* Variable
- jlinker-end Function
- jlinker-error Class
- jlinker-error-action Generic Function
- jlinker-error-data Generic Function
- jlinker-error-identifier Generic Function
- *jlinker-error-p* Variable
- jlinker-init Function
- *jlinker-init* Variable
- *jlinker-java-home* Variable
- jlinker-listen Function
- *jlinker-port-timeout* Variable
- jlinker-query Function
- *jlinker-reply-timeout* Variable
- *jlinker-request-timeout* Variable
- *jlinker-retry-delay* Variable
- *jlinker-retry-number* Variable
- *jlinker-run-java* Variable
- *jlinker-set-accessible* Variable
- jlinker-slot Generic Function
- *jlinker-start-timeout* Variable
- *jlinker-timestamp* Variable
- *jlinker-unix-vector-p* Variable
- *jlinker-verbose* Variable
- jlinker-version Function
- *jlinker-version* Variable
- jlookup Generic Function
- jmake-new Function
- jmeth Function
- jmethod Function
- jnew Function
- jnew-array Function
- *jni-ld-path-p* Variable
- *jni-library* Variable
- jni-wrapper Class
- jnotify-lisp Generic Function
- jpool-slot Function
- jpool-statistics Function
- jpreload Function
- jquery-connection-pool Function
- jquery-handler Function
- jregister-handler Generic Function
- jstart-connection-pool Function
- jstatic Function
- kcons Function
- kmeth Function
- last-use Generic Function
- make-dist-object Generic Function
- make-immediate-object Generic Function
- multi-async-http-servlet Class
- new-servlet Function
- scan-java-api Function
- start-work Generic Function
- tran-struct Class
- with-java-environment Macro
- wrap-object Generic Function
- wrapped-object-p Generic Function

Copyright (c) Franz Inc. Lafayette, CA., USA. All rights reserved.

ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 11.0