All Manuals > LispWorks® User Guide and Reference Manual > 39 The LW-JI Package


setup-java-interface-callbacks Functions


Initializes the Java interface.




init-java-interface &key jvm-library-path java-class-path option-strings jni-env-finder java-virtual-machine class-finder class-loader-finder java-to-lisp-debugger-hook report-error-to-java-host send-message-to-java-host => result

setup-java-interface-callbacks &key class-finder java-to-lisp-debugger-hook report-error-to-java-host send-message-to-java-host => t

A string, t or nil.
A string or a list of strings.
A list.
A function designator, or nil.
A foreign pointer of type java-vm-poi, or t.
A function designator or nil.
A function designator or nil.
A function designator or nil.
A function designator or nil.
A function designator or nil.
t or the keyword :no-java-to-lisp.

The function init-java-interface needs to be called before using any of the run time part of the Java interface. That includes the interface functions that are documented as requiring Java, and any of the user-defined callers. The definers in general do not need running Java, but the importing interface does.

Note: On Android and in dynamic libraries that were delivered with setup-deliver-dynamic-library-for-java with init-java true (the default), init-java-interface is called automatically by the system initialization, so you do not need to (and must not) call it.

init-java-interface may be used to first initialize the Java Virtual Machine (JVM) or can be called with the JVM already running.

Initializing the virtual machine

If init-java-interface needs to initialize the JVM, it must be called with jvm-library-path either t or the path of a dynamic library, and jni-env-finder must be nil. When jvm-library-path is t, init-java-interface uses a default JVM library path, which is currently "/System/Library/Frameworks/JavaVM.framework/JavaVM" on macOS and "-ljvm" on other Unix variants. On Windows, init-java-interface checks the registry for the location of the JVM library, using the keys that Oracle document in Java 2 Runtime Environment for Microsoft Windows . The library must implement the JVM, which means exporting the JNI functions, and to be able to find any supporting files that it may need. It loads this library by fli:register-module, and then initializes it using JNI_CreateJavaVM. The keyword arguments java-class-path and option-strings can be used to pass options to JNI_CreateJavaVM. Except on macOS, passing jvm-library-path t can work only if the library path contains the JVM library.

java-class-path specifies the class path(s) for additional classes on top of the system ones. It is used to specify the -Djava.class.path option. If java-class-path is a string, it is passed as is, and may contain more than path separated by the appropriate separator (#\: on Unix, #\; on Windows), for example "/myhomedir/myjavaclass;/systemdir/systemjavaclasses/". If it is a list, each string should be a path. Each path needs to specify either a directory containing JAR files, or a full path of a JAR file.

If you want to make calls from Java to Lisp, you will need to have the Java class com.lispworks.LispCalls. com.lispworks.LispCalls is defined in the JAR file lispcalls.jar which is part of the LispWorks distribution in the etc directory, that is (lispworks-file "etc/lispcalls.jar"), so this JAR file will have to be on the path. If you develop for Android and want to import Android classes, you will need the android.jar on the path too.

option-strings can be used to pass options to JNI_CreateJavaVM. Each element of option-strings is either a string or a cons of two strings. An element which is a string is passed as the option string (slot optionString of the JavaVMOption C struct). For a cons, the car is passed as the option string, and the cdr as the extra info (slot extraInfo in the JavaVMOption). Note that that you should not use the option -Djava.class.path when using java-class-path.

java-class-path and option-strings are ignored when init-java-interface is called after the JVM started.

Calling with JVM already running

If init-java-interface is called with the JVM already running, then jvm-library-path must be nil, and either jni-env-finder or java-virtual-machine must be supplied as non-nil, except when called inside a dynamic library delivered with setup-deliver-dynamic-library-for-java, when all arguments can be nil.

If jni-env-finder is non-nil then it must be a function of no arguments that returns a pointer to the JNI environment for the current thread. The result of the finder must be a foreign pointer of type jni-env-poi, corresponding to the C pointer JNIEnv*. The finder function needs to cope with being called on any thread and the result needs to be valid until that thread dies, at which point implementing code must deal with eliminating it. In general, this function needs to know how to find the Java virtual machine, and then use the JNI functions AttachCurrentThread or GetEnv.

If jni-env-finder is nil, then java-virtual-machine is used. If java-virtual-machine is t, LispWorks tried to find the Java virtual machine by first calling get-host-java-virtual-machine, and if this returns nil, by calling JNI_GetCreatedJavaVMs. Otherwise, java-virtual-machine is used as the virtual machine and must be a foreign pointer of type java-vm-poi, corresponding to the C type JavaVM*.

When running in a dynamic library delivered with setup-deliver-dynamic-library-for-java, init-java-interface should be called with jvm-library-path, jni-env-finder and java-virtual-machine all nil because the Java virtual machine is obtained using get-host-java-virtual-machine in this case.


The simple option when the JVM is already running is just passing java-virtual-machine t. However, the function that the system uses, JNI_GetCreatedJavaVMs, is a relic from the time when Java allowed more than one Java VM in each process, which it no longer allows. So in principle some day it may be eliminated (Android already does not define it, but on Android the system calls init-java-interface with jni-env-finder, so this does not matter). On the other hand it is documented in the latest version (8) without any indication that it is deprecated.

You may have a pointer to the Java VM to pass to init-java-interface either because you got it from code that started the Java VM (by JNI_CreateJavaVM), or by exporting JNI_OnLoad from a dynamic library. However, it is not a good idea to export JNI_OnLoad as a foreign callable from LispWorks when it is delivered as a dynamic library, because it will have to wait until LispWorks finished initialization. See 15.7 Loading a LispWorks dynamic library into Java.

By default, setup-deliver-dynamic-library-for-java sets up automatic initialization of the Java interface on startup. You need (and can call) init-java-interface in such a dynamic library only if you passed nil for the init-java argument to setup-deliver-dynamic-library-for-java.

If you call init-java-interface with jvm-library-path, jni-env-finder and java-virtual-machine all nil and get-host-java-virtual-machine returns nil, then init-java-interface returns nil rather than give an error.

Description: (cont.)

class-finder specifies a class finder function to be used if the normal search fails. It must be a function taking a string argument, and return a jobject representing a class for this string (for example, a caller for the method java.lang.Class.forName does the right thing). It is useful when the application knows how to find classes which are not visible from the system class loader. On Android, class-finder is passed with a function that calls java.lang.Class.forName with the application Class loader, which will find all classes in the application.

class-loader-finder is used when initializing the LispCalls. If class-loader-finder is non-nil, it must be a function of no arguments that returns a ClassLoader jobject. It is called once during initialization, and the result is stored to be used to find the interfaces when initializing a proxy definition. On Android, it is passed with a function that returns the application class finder. You need to be a Java expert to use this option. If class-loader-finder is nil, the Java method ClassLoader.getSystemClassLoader is used.

java-to-lisp-debugger-hook, when supplied, must be either a function of one argument or nil. When it is a function, it will be called when the debugger is invoked inside a call from Java to Lisp. The argument is a cl:condition object describing the problem. The function needs to do something to inform the user of the problem but not actually interactively, and return. The caller will then return a default value to Java. By default there is a hook that logs a bug form (by log-bug-form) and prints a message to the console. On Android, it is set to a function that logs the error and then invokes the user Java error reporters (set in Java by com.lispworks.Manager.setErrorReporter and com.lispworks.Manager.setGuiErrorReporter, see the documentation for setErrorReporter).

report-error-to-java-host, when supplied, must be a function of two arguments, both of which are strings. When it is passed, if the function report-error-to-java-host is called it uses this function to actually do the report. The first argument is assumed to the error string and the second a filename where there is a bug form, or nil. The function should report to the Java host, whatever that actually means. This keyword is used by the Android interface to set a function that calls into the Android Java code and invokes the same user Java error reporters that are used for the debugger hook above.

The system does not call report-error-to-java-host itself, so the context in which the function may be called is defined by your calls to it. However, it is intended to be used in error handlers, which means it should be able to cope with any context. The default function just prints to cl:*terminal-io*, which may be useful enough when just debugging.

send-message-to-java-host, when supplied, must be a function of two arguments: a string which is the message and a keyword, which tells it how to deal with it. The intent is to modify the "messages output" as described for the where-keyword in send-message-to-java-host. The meaning of "messages output" and the actual behavior is up to the function. On Android it is supplied a function that ends up calling the method com.LispWorks.Manager.addMessage. The default function checks the keyword and then writes the string to cl:*terminal-io*, which is probably good enough for testing purposes.

init-java-interface returns either t for success, or :no-java-to-lisp when it is successful but failed to initialize Java-to-Lisp calls, so you cannot call from Java to Lisp or use Lisp proxies. This failure would normally mean that it failed to find the class com.lispworks.LispCalls.

setup-java-interface-callbacks can be called after init-java-interface was called to change the values of class-finder, java-to-lisp-debugger-hook, report-error-to-java-host or send-message-to-java-host. This is useful in the situations where LispWorks performs the call to init-java-interface, which happens in Android and in a dynamic library delivered with setup-deliver-dynamic-library-for-java.

See also

15 Java interface

LispWorks® User Guide and Reference Manual - 01 Dec 2021 19:30:46