PackageDescription: JNIPort(Bundle)


JNI Port

Last published: March 14, 2008 by 'JoachimGeidel'


This bundle contains an interface to the Java Virtual Machine. It is based in the Java Native Interface (JNI). The original implementation was done by Chris Uppal (chris.uppal@metagnostic.org) in Dolphin Smalltalk. The bundle contains a port of this implementation to VisualWorks. Documentation is online at
http://jniport.wikispaces.com/
The documentation is work in progress. You can monitor updates by subscribing to the RSS feed at
http://jniport.wikispaces.com/space/xmla?v=rss_2_0

To use JNIPort, you also need the bundle JNIPort Prerequisites (version 0.43 or later), the package Registry (version 23 or later), the package FastCMethodPointers (version 1.1 or later) and the package WeakCollections (version 2 or later) from the Cincom Public Repository. Tests for JNIPort are available in the bundle JNIPort Tests.

This bundle contains two archives with additional files:

JNIPort_Extras.zip This archive contains the following files:
DolphinJNIHelper.dll and its source code
This library is needed for handling the hook functions provided by JNI for monitoring the state of a running JVM. See the package comment of ''CU JNI Helper''.
Currently only available for MS Windows.
JNIPort.jar and its source code in JNIPort.zip
This Java library is necessary for handling callbacks from Java to Smalltalk.
JNIPort-Tests.jar and its source code in JNIPort-Tests.zip
This Java library contains classes which are needed for running the tests in the bundle ''JNIPort Tests''.
JNIPort_Docs.zip This archive contains the original JNIPort documentation written by Chris Uppal. Please read this before using JNIPort.

Both files are taken from the Dolphin Smalltalk version of JNIPort at http://www.metagnostic.org/DolphinSmalltalk/JNIPort-Complete.zip
The documentation is the same as the one at the following URL: http://www.metagnostic.org/DolphinSmalltalk/JNIPort.html

Licence (from Chris Uppal):
"The terms under which you may use the software from this site are:

* You must not claim that you wrote it.
* You must not claim that you own it.
* You use it at your own risk.

So you can pretty-much use it for what you like, provided you are honest about it. "

Example for using JNIPort with automatic generation of wrapper classes and methods for Java classes (when evaluating the code, ignore warnings about undefined selectors):
------------------------------------------------[
| jvmSettings jvm zfClass zipfile entries |

jvmSettings := (JNIPort.JVMSettings new)
name: ''JVM with ghost class generation'';
yourself.
jvmSettings usesGhosts: true.
jvmSettings jniPortSettings useJNIHelperLibrary: false.

jvm := JNIPort.JVM newWithSettings: jvmSettings.

zfClass := jvm findClass: #''java.util.zip.ZipFile''.
zipfile := zfClass new_String: ''MyZipFile.zip''.
zipfile size_null. "--> answers an Integer"

entries := zipfile entries_null.
entries asAnEnumeration do: [:each | Transcript cr; print: each].
]------------------------------------------------

Some low level examples (for more and better examples see the documentation):

Computing "-321.2d abs" the hard way:
------------------------------------------------[
| vmargs env math status |
vmargs := JNIPort.JavaVMInitArgs new.
vmargs
addOption: ''-verbose:jni'';
ignoreUnrecognized: true.
[env := JNIPort.JNILibrary new createFirstJNIEnv: vmargs]
on: JNIPort.JNIError
do: [:error | ^Dialog warn: (''JVM startup failed with: '', ( JNIPort.JNILibrary lookupErrorCode: error parameter))].
math := env
FindClass_name: ''java/lang/Math''
onException: [:error | ^Dialog warn: (''Error accessing Math because: '', error description)].
math externalData isValid ifTrue:
[ | absID |
absID := env
GetStaticMethodID_class: math
name: ''abs''
sig: ''(D)D''
onException: [:error | ^Dialog warn: (''Error accessing abs because: '', error description)].
absID externalData isValid ifTrue:
[| arguments result |
arguments := JNIPort.JNIValueArray fromArray: #(-321.2d) types: #(jdouble).
result := env
CallStaticDoubleMethodA_class: math
methodID: absID
args: arguments
onException: [:error | ^Dialog warn: (''Error performing abs because: '', error description)].
Transcript cr; show: ''Result: ''; print: result; cr; endEntry]].

"After executing the following expressions, you will have to restart the image to be able to work with a Java VM again."
status := env javaVM DestroyJavaVM.
status ~= JNIPort.JNIInterface current JNI_OK
ifTrue: [Dialog warn: ''JVM shutdown failed with: '', (JNILibrary lookupErrorCode: status)].
]------------------------------------------------

Computing "-3 abs" the really hard way, doing everything at the lowest possible level of JNIPort code:
------------------------------------------------[
| jni options option vmargs jvm jvmPointer env envPointer status destroyJavaVM findClass math |
jni := JNIPort.JNIInterface current.

options := jni JavaVMOption gcCalloc: 1.
option := (options refAt: 0).
option memberAt: #optionString put: ''-verbose:jni'' gcCopyToHeap.
option memberAt: #extraInfo put: nil.

vmargs := jni JavaVMInitArgs gcMalloc.
vmargs memberAt: #version put: jni JNI_VERSION_1_4.
vmargs memberAt: #nOptions put: 1.
vmargs memberAt: #options put: options.
vmargs memberAt: #ignoreUnrecognized put: jni JNI_TRUE.

jvm := jni JavaVM gcMalloc.
jvmPointer := jvm type newPointerToAddress: jvm referentAddress.
env := jni JNIEnv gcMalloc.
envPointer := env type newPointerToAddress: env referentAddress.

status := jni JNI_CreateJavaVM: jvmPointer with: envPointer with: vmargs.
jvm := jvmPointer contents.
env := envPointer contents.

status ~= jni JNI_OK ifTrue: [^Dialog warn: ''JVM not started: '', status printString].
nil halt.

findClass := env contents memberAt: #FindClass.
math := findClass callWith: env with: ''java/lang/Math'' asParameter.

math isValid ifTrue:
[ | arguments getMethodID absID result |
getMethodID := env contents memberAt: #GetStaticMethodID.
arguments := Array new: 4.
arguments
at: 1 put: env;
at: 2 put: math;
at: 3 put: ''abs'' asParameter;
at: 4 put: ''(I)I'' asParameter.
absID := getMethodID call: arguments.

absID isValid ifTrue:
[| callMethod |
callMethod := env contents memberAt: #CallStaticIntMethod.
arguments := Array new: 4.
arguments
at: 1 put: env;
at: 2 put: math;
at: 3 put: absID;
at: 4 put: -3.
result := callMethod call: arguments.

Transcript cr; show: ''Result: ''; print: result; cr; endEntry]
].

"After executing the following expressions, you will have to restart the image to be able to work with a Java VM again."
destroyJavaVM := jvm contents memberAt: #DestroyJavaVM.
status := destroyJavaVM callWith: jvm.
status ~= jni JNI_OK ifTrue: [^Dialog warn: ''JVM not destroyed: '', status printString].
]------------------------------------------------

Querying the JNI version:
------------------------------------------------[
| vmargs env status version |
vmargs := JNIPort.JavaVMInitArgs new.
vmargs
addOption: ''-verbose:jni'';
ignoreUnrecognized: true.

[env := JNIPort.JNILibrary new createFirstJNIEnv: vmargs]
on: JNIPort.JNIError
do: [:error | ^Dialog warn: (''JVM startup failed with: '', (JNIPort.JNILibrary lookupErrorCode: error parameter))].

version := env GetVersion.
Transcript show: ''JNI version: ''; print: (version printStringRadix: 16); cr; endEntry.

"After executing the following expressions, you will have to restart the image to be able to work with a Java VM again."
status := env javaVM DestroyJavaVM.
status ~= JNIPort.JNIInterface current JNI_OK
ifTrue: [Dialog warn: ''JVM shutdown failed with: '', (JNILibrary lookupErrorCode: error parameter)].
]------------------------------------------------

Querying the JNI version the hard way:
------------------------------------------------[
| jni options option vmargs jvm jvmPointer env envPointer status destroyJavaVM getVersion version |
jni := JNIPort.JNIInterface current.

options := jni JavaVMOption gcCalloc: 1.
option := (options refAt: 0).
option memberAt: #optionString put: ''-verbose:jni'' gcCopyToHeap.
option memberAt: #extraInfo put: nil.

vmargs := jni JavaVMInitArgs gcMalloc.
vmargs memberAt: #version put: jni JNI_VERSION_1_4.
vmargs memberAt: #nOptions put: 1.
vmargs memberAt: #options put: options.
vmargs memberAt: #ignoreUnrecognized put: jni JNI_TRUE.

jvm := jni JavaVM gcMalloc.
jvmPointer := jvm type newPointerToAddress: jvm referentAddress.
env := jni JNIEnv gcMalloc.
envPointer := env type newPointerToAddress: env referentAddress.

status := jni JNI_CreateJavaVM: jvmPointer with: envPointer with: vmargs.
jvm := jvmPointer contents.
env := envPointer contents.

status ~= jni JNI_OK ifTrue: [^Dialog warn: ''JVM startup failed with: '', status printString].

getVersion := env contents memberAt: #GetVersion.
version := getVersion callWith: envPointer.

Transcript show: ''JNI version: ''; print: (version printStringRadix: 16); cr; endEntry.

destroyJavaVM := jvm contents memberAt: #DestroyJavaVM.
status := destroyJavaVM callWith: jvm.
status ~= jni JNI_OK ifTrue: [Dialog warn: ''JVM shutdown failed with: '', status printString].
]------------------------------------------------