Updated 11 March 2008
Introduction
JavaConnect is a Visualworks Smalltalk library that allows a seamless interaction between Smalltalk and Java. A Smalltalk application can access any Java object and send messages to it, just as if it were a Smalltalk object. Its implementation relies on a connection between the Smalltalk environment and a standard Java VM environment using Visualworks' DLLCC and Java's JNI. The Java application thus executes on a regular Java VM and the Smalltalk application executes on the regular Smalltalk VM.
JavaConnect was developed to enable SOUL to reason over Java programs without reimplementing many of the readily available Java libraries and utilities that exist for this purpose. In particular, we are using the Java libraries Soot and the Eclipse JDT to reason about Java programs from within Smalltalk. JavaConnect makes these Java utilities and libraries available as Smalltalk libraries, and thus makes them directly usable by tools implemented in Smalltalk. Cava is the SOUL extension containing a set of logic rules and all required functionality to use the abovementioned Java libraries. JavaConnect was inspired by the JNIPort for Dolphin and JavaInSt work. The JNIPort now also has a Visualworks version, but we also continue to maintain and develop JavaConnect. Please feel free to explore, test and use this work!
JavaConnect was used to demonstrate the advantages of computational reflection in dynamic programming languages. Check out the presentation that was used during the course on reflection in programming languages.
JavaConnect is released under the MIT Open Source License.
Download and Get Started!
JavaConnect is written for Visualworks Smalltalk, works with the latest Java versions and has been tested on Mac (PPC and Intel), Linux and Windows.
-
Load the latest version of the JavaConnect bundle that was blessed at least as a Development version from the DMP store. The DMP store is accessible from Visualworks Smalltalk using the following store profile:
environment: prog2.vub.ac.be:5432_dmpstore login: guest password: guest -
The settings tab is currently not functioning- Open the settings tab of JavaConnect in the Visualworks Settings tools and verify if the host platform is correctly detected. You can also select a VM configuration (i.e. Java VM startup options, such as the classpath) which will be displayed as the default configuration when starting the VM. In the future, different configurations can be created and changed here. For now, they have to be created manually (see later).
-
JavaConnect uses a small Java utility library created by Cristiano Sadun. Previous versions required you to download the jar file and put it on the classpath. From now on, the jar file is packaged with JavaConnect and is automatically generated on the classpath when you start it for the first time. You will be notified about this when it happens.
-
To start JavaConnect (actually to start a Java VM), click the Java duke icon in the Visualworks launcher toolbar. A list of possible configurations will pop up. A configuration determines the parameters used to start a Java VM. The most important parameter of a configuration is the classpath.
If you loaded JavaConnect without any of the applications that use it, you will only see a default and a test configuration. The test configuration allows you to run the unit tests, while the default configuration starts the Java VM with an empty classpath (it only contains a small jar file that is bundled with JavaConnect itself).
You can create your own configurations, which is necessary if you want to use other Java libraries than those made available by existing JavaConnect applications. Read more about creating your own configurations below.
-
After a succesful startup, a dialog window will appear confirming the succesful start. The next time you click on the duke icon, the JavaConnect Browser will open up. You can use this browser to browse the available packages, load and view classes in packages, browse the system properties, etc. However, it is currently a mostly obsolete piece of JavaConnect because all loaded Java packages and classes can be browsed in the standard Visualworks refactoring browser. It is important to note that JavaConnect loads classes lazily, i.e. a Java class is loaded into the Smalltalk environment when you try to use that class. This is why so few classes can be seen in the browser after you freshly started JavaConnect.
- Read through the basics below to get acquainted.
- Enjoy and check out why we have developed JavaConnect and play with some interesting applications that use it: Penumbra
- If you plan to use JavaConnect for your development, we would like to hear about it.
Basics
Java packages
JavaConnect maps Java packages onto Visualworks' namespaces beneath the JavaWorld root namespace. The java package java.lang.util, for example, is represented by the JavaWorld.java.lang.util namespace. For convenience, the java sub-namespace has been imported into the Smalltalk namespace, allowing to omit a reference via JavaWorld to refer to classes and sub-namespaces of the java namespace from any Smalltalk code.
In addition, the refactoring browser's package tab shows the java packages that are currently available on the classpath of the loaded and running Java VM. These Java packages are not to be confused with the Smalltalk packages and are therefore rooted in JavaWorld keyword instead of the Local Image keyword.
Java classes
Regular Java classes are represented using regular Smalltalk classes with the same name and are accessible via the namespace that represents their corresponding Java package. The Java Date class, for example, is accessible as JavaWorld.java.lang.util.Date . Member classes are also represented using regular Smalltalk classes but their name is prefixed with their parent class(es) name(s) and includes the separator '_' between all the names. The Iterator class, member class of the List class in the java.lang.util package, for example, is represented by the JavaWorld.java.lang.util.List_Iterator class in Smalltalk.
The refactoring browser displays the list of classes contained in a Java package in the class tab of the refactoring browser.
Java objects
Java objects can be created by invoking the constructors defined on the class via different variants of the new message. This is explained in the next section. The resulting Java objects can be manipulated like any Smalltalk object. Java objects are, of course, also obtained as return results from the invocation of Java methods. JavaConnect guarantees that identical Java objects are uniquely represented in the Smalltalk image. This means that object comparison of Java objects in Smalltalk works and that the same actual Java object (living in the Java VM) is consistently represented by a unique Java object in the Smalltalk image.
Java objects can be inspected using the regular Smalltalk inspectors.
Java methods
The public methods of a Java class are represented as regular Smalltalk methods, except for a pre-defined and automatic conversion from Java's method-name syntax onto Smalltalk's message syntax. This means that the Java methods are available as regular Smalltalk methods. Furthermore, similar to any Smalltalk class, the static methods of the Java class can only be invoked on the class itself, while the normal methods can only be invoked on instance objects of the class.
The method-name conversion scheme is as follows:
- A Java method without arguments is trivially mapped onto a Smalltalk method that implements a unary message. The unary message exactly corresponds to the Java methodname. For example, the Java method with signature int length() is represented in Smalltalk as the method with selector length.
- A Java method with one or more arguments is mapped onto a Smalltalk method having a keyworded header. The first keyword is the concatenation of the Java methodname and the short name of the type of the first arguments, separated by a '_'. The remaining keywords are the short names of the types of the remaining arguments. For example, the Java method with signature int lastIndexOf(String str, int fromIndex) is represented by the Smalltalk method that implements the selector lastIndexOf_String:int: .
- In some cases, notably in the case of overloaded methods, this naming conversion can result in different Java methods having the same header in Smalltalk. JavaConnect prevents this from happening and expands the simple names of the types in each keyword with their corresponding packagename. Note that this only happens on rare occassions where methods are overloaded using types having the same simple names, but are from different Java packages
The refactoring browser displays all methods of a Java class in the 'native Java methods' protocol of the class. Instance methods and class (static) methods are displayed in their corresponding tab.
Java constructors
The public constructors of a Java class are mapped onto corresponding Smalltalk class methods. The name conversion scheme for constructors is identical to the one for methods, but the name of a constructor is always mapped onto 'new'. This means that zero-argument constructors are mapped onto the new class-method and multiple argument constructors are mapped onto keyworded messages of which the first keyword is prefixed with 'new'. For example, the Java constructor Date(int year, int month, int date) is represented as the Smalltalk class method with selector new_int:int:int: (implemented on the Date class).
The constructors of each Java class are displayed by the refactoring browser as class methods in the 'java native constructors' protocol.
Java fields
Public fields of a Java object are made available by automatically generated accessors and mutators. The accessor and mutator methods are prefixed with 'get_' and 'put_' respectively, following the name of the field. Static fields are accessible via class methods, while instance fields are accessible via instance methods.
Typing
Smalltalk programmers are only confronted with Java's static typing through the names of the messages that are sent to Java objects and classes. As we explained, these names are composed of the simple type names of each method parameter. From within Smalltalk, we are not concerned about the static return type of the method we invoke because JavaConnect always returns an object of the dynamic return type of the method. Of course, we must also ensure to pass the right argument objects to the methods, as the Java VM will throw a typing exception otherwise. But, this is no different from invoking a Smalltalk method. The current version of JavaConnect does not allow to pass Smalltalk objects to Java methods, but the upcoming version will allow to pass any kind of Smalltalk object to Java and even have Java invoke Smalltalk methods.
Conversion of Java values into Smalltalk values, and vice-versa.
As Java makes a difference between native and reference types, JavaConnect also differentiates between these two kinds of values.
All native values (int, float, double, char, ...) are automatically converted into their Smalltalk counterparts and vice-versa. You can thus pass any such Smalltalk value where a native Java value is expected and you will get a Smalltalk value back when a method returns a native type.
All reference values (i.e. class types), are represented in Smalltalk as instances of their corresponding classes. For example, a Java String object will thus not be converted automatically into a Smalltalk String object but will be represented as an instance of the JavaWorld.java.lang.String class.
In order to convert a Java String object into a corresponding Smalltalk String object, you can send the #asSmalltalkValue message to the Java String object. This pre-defined Smalltalk method on the Java String class will return a new Smalltalk String object with the same textual content as the Java String object. Inversely, it is possible to pass a Smalltalk String object where a Java String object is expected. This is because JavaConnect always tries to convert any Smalltalk object being passed to a Java method into a corresponding Java object. This is done by invoking the #asJavaValue message on the Smalltalk object, which should result in an appropriate Java object. JavaConnect includes a predefined implementation for Smalltalk values of String, Boolean, Integer, Character, Array, and many more. Just take a look at the implementors of #asJavaValue.
Developers can also implement custom #asSmalltalkValue and #asJavaValue methods on any Java or Smalltalk class using the prototyping mechanism, which is explained later on.
Interfaces?
JavaConnect does not care about Java interfaces. The only exception being for values of anonymous member classes, which are then represented in Smalltalk as objects of the static return type of the method through which they are obtained. As this static return type can be an interface type, an interface can appear as a Smalltalk class in the image.
Java Exceptions
Exceptions thrown by the Java methods are converted into Smalltalk exceptions containing an instance of JavaWorld.java.lang.Throwable. The mechanism is quite basic at this moment and is bound for improvement in the future.
Adding Smalltalk code and state to Java classes
JavaConnect features a mechanism of so-called 'prototypes' which allow to add Smalltalk methods to the represented Java classes in our image. This mechanism is bound to change, so we will include further information about it in the future. In the meantime, you can, for example, note the implementation of some Smalltalk methods defined on the common superclass of all Java classes (i.e. JavaObject) and in the JavaConnect package 'JavaConnect-JavaClassExtensions'.
Passing Smalltalk objects to Java methods
Will be possible in the upcoming version.
Java calls backs to Smalltalk
Will be possible in the upcoming version.
System.out and System.err
JavaConnect redirects the JVM's System.out and System.err printstreams so you can access them from Smalltalk.
Java VM configurations
When you start JavaConnect, the JVM is started with a set of options that correspond to the command-line options you can pass to the 'java' executable. The most important option of a configuration is the classpath parameter. It determines which Java classes you can load and use. A fresh download of JavaConnect contains some pretty useless configurations with empty classpaths. This means that only the standard Java classes are available. Fortunately, you can create your own configurations very easily.
The class JavaVMOptions has a class-side protocol called 'predefined configurations'. Each method in this protocol defines a configuration. You can add a configuration by defining a new method in this protocol. Upon execution, the method MUST return a JavaVMOptions instance that holds all desirable options for your configuration. Take a look at the existing methods how to name your configuration and how to add options to it. The most important option, the classpath, has a convenience method to set it using a string.
Applications that use JavaConnect provide more configuration examples. Check those out if you need more In the future, we also plan to implement a settings tool.
Screenshots (click to enlarge)
Browsing Java classes and their methods in the standard Smalltalk Refactoring Browser. |
|
A preliminary version of the JavaConnect settings tab in the Visualworks settings. In the current version it allows to change the library locations but is still bound to change in the future. |
|
JavaConnect loaded in the Visualworks Smalltalk environment ! |
Some Code Examples
Let's illustrate some usages of JavaConnect with some code examples.
- The zipfile example adapted from the JNIPort work:
zipfile := JavaWorld.java.util.zip.ZipFile new_String:'foo.zip'. zipfile size inspect. entries := zipfile entries. [entries hasMoreElements] whileTrue:[Transcript show: entries nextElement]
- Enabling the hashing of Java objects in Smalltalk collections,
including caching the hash value. The following method is implemented on
the common superclass of all classes that represent Java classes:
JavaObject>>hashCode hashcode ifNil:[hashcode := self hashCode]. ^ hashcode
- Much of the code of JavaConnect is actually using Java libraries, such
as the java.lang.reflect package. This means
JavaConnect is written in terms of itself and merely bootstraps some essential
classes and methods at startup. This is a piece of code from the implementation
of JavaConnect that is part of the code that generates methods defined
on a Java class. In particular, it gathers the methods defined on a class
and separates the instance and class (static) methods before starting the
code generation.
For clarity, the parts that actually invoke Java code are in bold.
| instancemethods classmethods selector signature methodmodifiers | instancemethods := Dictionary new. classmethods := Dictionary new. "First group all methods with identical 'standard' selectors. We also separate class and instance methods" self javalangClass getMethods do: [:method | signature := self javaMethodSignatureFor: method. selector := signature smalltalkSelector. methodmodifiers := method getModifiers. (java.lang.reflect.Modifier isAbstract_int: methodmodifiers) ifFalse: [(java.lang.reflect.Modifier isStatic_int: methodmodifiers) ifTrue: [(classmethods at: selector ifAbsentPut: [OrderedCollection new]) add: signature -> method] ifFalse:[(instancemethods at: selector ifAbsentPut: [OrderedCollection new]) add: signature -> method]]]. "add all instance methods to our methoddictionary" self addJavaMethods: instancemethods to: self. "add all class methods to our metaclass' methoddictionary" self addJavaMethods: classmethods to: self class.
Need to know more?
If you're interested to learn about the implementation, or if you want to make adaptations, or anything else: Please contact me
JavaConnect
:= Java asSmalltalkValue