ToCDocOverviewCGDocRelNotesIndexPermutedIndex
Allegro CL version 6.2
Moderately revised from 6.1

jLinker - A Dynamic Link between Lisp and Java

This document contains the following sections:

1.0 Introduction
2.0 Creating a Dynamically Linked Lisp/Java Application
   2.1 Calling Java
      2.1.1 Two Calling Models
      2.1.2 Data Types and Conversions
   2.2 Dynamic Linkage Lisp Reference - The Funcall Model
   2.3 Dynamic Linkage Lisp Reference - The Class Model
   2.4 Dynamic Linkage Java Reference
   2.5 Initialization Functions and Variables
   2.6 Event Handling
      2.6.1 Lightweight Callback to Lisp Methods
      2.6.2 Lisp Functions to Dispatch Java Events
      2.6.3 Implemented Sub-Classes of AWT Event Handlers
   2.7 I18N Issues
   2.8 Java Applets
   2.9 Re-entrancy
   2.10 jLinker Connect Issues
   2.11 Calling Methods of Inner Classes
3.0 Installation
   3.1 Files Involved in Installing jLinker
   3.2 Dedicated Java Server
   3.3 Peer-to-Peer Interaction
   3.4 One Lisp and Several Java Client/Server connections
4.0 A Complete Code Example
5.0 Packaging Lisp applications as Java beans and servlets
   5.1 The jLinker Java Bean API
   5.2 The jLinker Servlet API

The version of jLinker with Allegro CL 6.2 is 3.2.2 or higher. The Java code (in jlinker.jar) is version 3001001 or higher. See jlinker-version for information on how to determine the version you are currently using.



1.0 Introduction

The purpose of this tool is to automate the interfacing of Lisp programs to Java class libraries. We have accomplished this goal with the use of remote interfaces supported by a proprietary socket connection.

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.

General callbacks from Java to Lisp are accomplished through the invokeInLisp method in class JavaLinkDist. To facilitate some commonly used callbacks from Java to Lisp we provide a framework that includes an object index, lightweight scalar messages, and 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 javatools.jlinker package. You may want to use this package so symbols need not be qualified with javatools.jlinker:. Do this by evaluating (use-package :javatools.jlinker). See use-package.

The jLinker module is loaded by evaluating:

(require :jlinker)

;;;  Allegro Enterprise customers may 
;;;  load additional features
;;;  (see Section 5.0 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.



2.0 Creating a Dynamically Linked Lisp/Java Application


2.1 Calling Java

Once the client-server interfaces have been established, 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

   (jcall (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 correspnd 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") ())

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 appropriate transfer objects using default rules listed in the table below. On the Java side the transfer objects are transformed into Java values or objects as shown in the table.

In Java, values must be prepared for transfer to Lisp by calling the static method newDistOb(). The table below shows which Java types are allowed in calls to newDistOb().

Default Conversions
Lisp Type -> Java Type -> Lisp Type
nil null nil
character char character
None (see Note 1) byte integer
None (see Note 1) short integer
integer int integer
None (see Note 1) long integer
None (see Note 1) float double-float
single-float double double-float
double-float double double-float
None (see Note 1) boolean t or nil
string String string
None (see Note 1) byte[] (array (signed-byte 32) *) (see Note 3)
None (see Note 1) short[] (array (signed-byte 32) *) (see Note 3)
(array (signed-byte 32) (*)) int[] (array (signed-byte 32) *)
None (see Note 1) float[] (array double-float *) (see Note 3)
(array (double-float 32) (*)) double[] (array double-float *)
None (see Note 1) String[] (array t *) (see Note 3)
None (see Note 1) Object tran-struct (see Note 2)
symbol (see Note 4) TranStruct symbol
Lisp object (see Note 5) TranStruct Lisp object

Table Notes:

  1. There is no automatic conversion from Lisp to this Java type. The Java type can be passed with an explicit call to make-immediate-object.
  2. An arbitrary Java Object instance is passed to Lisp as a remote reference. A remote reference in Lisp is an instance of the tran-struct class.
  3. The java type must be coerced to int[] or double[] before calling newDistOb().
  4. A symbol is passed to Java as a remote reference. A remote reference in Java is an instance of the TranStruct class. If the remote reference is passed back to Lisp, the original symbol is seen.
  5. Arbitrary Lisp data is passed to Java as a remote reference. A remote reference in Java is an instance of the TranStruct class. If the remote reference is passed back to Lisp, the original Lisp object is seen.

When a default conversion will not send the correct data type to Java, the function make-immediate-object may be used to encode the type conversion more explicitly. The most common use of this function is to pass boolean values to Java since any Lisp data type may be treated as a boolean value.


2.2 Dynamic Linkage Lisp Reference - The Funcall Model

We use the following meta notations for certain arguments:

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 is used to build the pre-load expressions.


2.3 Dynamic Linkage Lisp Reference - The Class Model

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.


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.

All the methods below are defined in class JavaLinkDist.

Further boolean predicates on numbers and other operators

While integerP() returns true for any integer value (byte, short or int) the following predicates return true only when a specific type was encapsulated in an immediate object.

The following are also useful.

Java NOTE: There are no constructors in Java to generate a transfer object containing an array of byte, short, or float (and Lisp does not expect or recognize these). These arrays can be converted in Java to arrays of int or double and passed to Lisp that way.

The following were introduced in release 6.1:


2.5 Initialization Functions and Variables

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

If the file jlinker.jar is not visible in the current directory, a continuable error is signaled.

The functions and variables are:

The following functions and variables are typically defined in the file jl-config.cl. They are used when Java is started from Lisp. The definitions may need to be modified to fit the configuration of a particular site and Java version. If Java is not started from Lisp, then these variables and functions are ignored.


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 make 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.

----- Java ----------

public class MyWindowAdapter extends WindowAdapter {

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


----- Lisp ----------

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

The Java method may also call back to Lisp with invokeInLisp.


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


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) {
    int[] l = { e.getModifiers(), (e.isActionKey()?1:0), e.getKeyCode() };
    JavaLinkCommon.ltoj_anchor.callLisp
      ("keyTyped", (Object)(e.getComponent()), e.paramString(), 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() };
    
    JavaLinkCommon.ltoj_anchor.callLisp
      ("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

One Java VM can support exactly one connection to Lisp (because static variables are used extensively).

If Lisp calls Java then Java may call back to Lisp before returning from the initial call, but a call to Java from the callback will block until the initial call returns. This will typically lead to a deadlock.

If Java calls Lisp then Lisp may call back to Java, but a call to Lisp from the callback will block until the initial call to Lisp returns. This will typically lead to a deadlock also.

On the Lisp side, the variable javatools.jlinker::*transport-timeout* may be set to a positive number. This will trigger an error when a call to Java blocks for more than the specified number of seconds. If the number is larger than most reasonable delays in Java, this should detect most deadlock situations in the Lisp code. There is no corresponding timeout feature in Java.


2.10 jLinker Connect Issues

jLinker requires two open socket connections between Lisp and Java:

By default, Lisp listens for a connection from Java and that becomes the connection for calls from Java to Lisp; Java listens for a connection from Lisp and that becomes the connection for calls from Lisp to Java.

It is also possible for Lisp to listen and accept both connections.

How do Lisp and Java find each other?

Intranet:

Lisp and dedicated Java VM (Lisp starts the JVM)

Lisp and separate Java application

Lisp and applet must use EMBED or OBJECT tag to run Java plugin (APPLET tag signals random errors)

Internet:

The only secure connection is to connect to Lisp from a servlet. Servlet connection to Lisp is an intranet situation.

The other forms of connection will run into firewall problems.

Connecting from the Java side:

 // sample variable settings:
 String lispFile     = "";
 String lispHost     = "";
 int    lispPort     = 4321;
 int    pollInterval = 1000;
 int    pollCount    = 300;

 int    javaTimeout   = -1;
 String javaFile      = "";
 String javaHost      = "";
 int    javaPort      = 0;

 // use this to emit progress messages in Java
 com.franz.jlinker.JavaLinkCommon.sdebug = true;

Mode->		Lisp		Lisp		Java		Java
		advertises	advertises	advertises	advertises
		in file		at port		in file		at port

lispFile	A1		--		--		--
lispHost	--		B2		--		--
lispPort	--		C2		--		--
pollInterval	D1		D1		--		--
pollCount	E1		E1		--		--

javaFile	--		--		F3		--
javaHost	G1		G1		G3		--
javaPort	H1		H1		H3		H4
javaTimeout	--		--		I3		I3


Java call:	connect(lispFile, javaHost, javaPort, pollInterval, pollCount);

	--	ignored
	A1	The pathname for the file where Lisp advertises host:port
		"" denotes default value "JavaToLisp.trp"
	D1	If Lisp advertises, then java will poll every pollInterval
		milliseconds.
		If pollInterval<0, look for Lisp exactly once
	E1	If pollInterval>=0 count down the pollCount value and stop
		polling when negative
	G1	This is the host name transmitted to Lisp when a connection 
		is made to the advertising Lisp
		"" denotes the default host "localhost"
	H1	If this is 0, Java allocates a port and transmits the 
			port number to Lisp
		If this is >0, Java listens at that port and transmits
			the port number to Lisp
		If this is <0, Java assumes the absval is the port number 
			where Lisp is listening to connect to the Java server

		Meanwhile back in Lisp:
		(jlinker-init :lisp-advertises	:lisp-file non-nil
						[:lisp-host hh :lisp-port pp]
						[:java-port neg])
			Lisp writes the effective host:port into the file


Java call:	connect(lispHost, lispPort, javaHost, javaPort, 
			pollInterval, pollCount)

	B2	The hostname where Lisp is advertising 
	C2	The port number where Lisp is advertising

		Meanwhile back in Lisp:
		(jlinker-init :lisp-advertises	:lisp-file nil
						:lisp-port non-zero-non-neg
						[:lisp-host hh])



Java call:	advertise(javaFile, javaHost, javaPort, javaTimeout)

	F3	The pathname for the file where Java advertises host:port
		"" denotes default value "LispToJava.trp"
	G3	This is the host name transmitted to Lisp in the advert file
		"" denotes the default host "localhost"
	H3	This is the port number transmitted to Lisp in the advert file
		If this is 0, Java allocates a port and transmits the 
			port number to Lisp
		If this is >0, Java listens at that port and transmits
			the port number to Lisp
	I3	the number of milliseconds that Java will advertise
		-1 denotes forever

		Meanwhile back in Lisp:
		(jlinker-init :java-advertises ...)


Java call:	advertise(javaPort, javaTimeout)

	H4	This is the port number where Java will advertise,
		it must be >0 (since otherwise, Lisp would not know
		where to listen)
	I3	the number of milliseconds that Java will advertise
		-1 denotes forever

		Meanwhile back in Lisp:
		(jlinker-init :java-advertises	:lisp-file nil
						[:lisp-host hh :lisp-port pp]
						:java-file nil
						:java-host "hh"
						:java-port pp)

2.11 Calling Methods of Inner Classes

JLinker uses Java Reflection methods to make all the method call 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)))J
   (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)))J
   (jstatic "hasNext" "Wrap" it))

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



3.0 Installation


3.1 Files Involved in Installing jLinker


3.2 Dedicated Java Server

When the interface is to a dedicated Java server, the interface is setup and controlled entirely from the Lisp application.

To Prepare the Environment

To Prepare the Application

  1. Start Allegro CL, and make sure that jLinker is loaded.
  2. Compile application files with preload instructions. This will start the jLinker interfaces with a call to jlinker-init and update the preload file.
  3. Optionally, start the application and exercize it to uncover dynamic class and method references.
  4. Create a class and method preload file, with a call to (jlookup :gen-preload).

To Run the Application

  1. Start Allegro CL, and make sure that jLinker is loaded.
  2. Make sure the application is loaded.
  3. Start the jLinker interfaces with a call to jlinker-init.
  4. Start the application.

3.3 Peer-to-Peer Interaction

If the jLinker interface is to an independently running Java application, the steps needed to establish the interface need to be modified.

Lisp advertises, THEN, Java connects

     Advertise in (default) file:
     Lisp: (jlinker-init :lisp-advertises [:lisp-file path] [:timeout n] ...)

     Java: com.franz.jlinker.JavaLinkDist.connect(j2l, "", 0, -1, -1);

Lisp must advertise before Java issues the connect() call; otherwise the connect() call will fail (return false). Lisp advertises the port in the specified file.

To advertise for a limited time, call jlinker-init with :timeout n where n is the number of seconds to advertise.

Java keeps looking for Lisp to advertise

     Java: com.franz.jlinker.JavaLinkDist.connect(j2l, "", 0, 
                                                   pollInterval, pollCount);

     Lisp: (jlinker-init :lisp-advertises [:lisp-file path] [:timeout n] ...)

If Java makes the call first, then the Java program will keep checking every pollInterval (milliseconds) until Lisp starts or the count runs out.

If Lisp has made the call first, Java will connect immediately.

Java advertises, THEN, Lisp connects

     Java: com.franz.jlinker.JavaLinkDist.advertise(l2j, "", 0, -1)

     Lisp: (jlinker-init :java-advertises [:java-file l2j] ...)

Java must make the call first, otherwise, the call to jlinker-init will fail (return a list). Java advertises a port number in the given file that defaults to "LispToJava.trp", or Java may simply advertise at a pre-determined port known to the Lisp application.


3.4 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*.



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 :javatools.jlinker)
(defpackage :javatools.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 <Allegro directory>/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. These features are available only in the Allegro Enterprise edition.

The Enterprise Edition extensions of jlinker 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)   ;; Enterprise customers only.

The Enterprise Edition of 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. This feature is available only in the Allegro Enterprise edition.

All Lisp symbols are in package :javatools.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. This feature is available only in the Allegro CL Enterprise edition.

All Lisp symbols are in package javatools.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 javatools.jlinker::servlet class is the uperclass of all the Lisp implementation classes that support the servlet interface. It has slots:

The following functions and methods are defined:

The http-servlet class is a subclass of javatools.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 rpedefined dummy methods are:

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:

Two start-work methods are defined on instances of those classes. The argument list is (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.


Copyright (c) 1998-2002, Franz Inc. Oakland, CA., USA. All rights reserved.
Documentation for Allegro CL version 6.2. This page has had moderate revisions compared to the 6.1 page.
Created 2002.2.26.

ToCDocOverviewCGDocRelNotesIndexPermutedIndex
Allegro CL version 6.2
Moderately revised from 6.1