The goal of this project is to extend the Java-Smalltalk interface provided by ClassicBlend (by Applied Reasoning) to handle any custom Model/View/Controller application.
At present, ClassicBlend supports Smalltalk applications that have been built using the UIBuilder interface. Custom widgets can be added, but the process is long and complicated. Applications that include components which have not been defined as UIBuilder widgets are not supported at present.
We extend ClassicBlend so it can handle any Model/View/Controller application. This is achieved by defining ORB interactions for arbitrary Views on both the Smalltalk and Java sides. In addition to supporting widgets, the extended version of ClassicBlend supports basic geometric objects and wrappers.
1.1. ORB overview
The postscript document cbdoc.ps contains a detailed overview of ClassicBlend's framework for building widget proxies. It also discusses the structure and behavior of the underlaying ORB structure. This overview focuses mainly on the server since in general, the client side mirrors the Smalltalk implementation.
ClassicBlend's functionality if closely tied to that of the UIBuilder. CB has an ArcbBuilder class that has the same protocol as the UIBuilder, but instead of displaying visual objects on the screen, it sends messages over the ORB to the Java interface. This way, (almost) any application built using the UIBuilder can be executed with ClassicBlend.
The downside of this close coupling with the builder is that visual objects that have not been created by the UIBuilder cannot be linked to the Java interface using ClassicBlend.
In our extension to ClassicBlend, we implement additional functionality in the ORB, so that applications containing custom views can be executed with the Java interface. The process of extending ClassicBlend in this manner includes the following steps:
The above ensures that applications defining their own views would execute correctly under ClassicBlend.
3.1. Smalltalk subsystem
In applications based on the Model/View/Controller interface framework, custom views are usually incorporated by following these steps:
UIBuilder spec for an arbitrary view holder is the
ArbitraryComponentSpec. In order to enable the
ArcbBuilder to use this arbitrary view holder, the spec's
dispatchTo:with: method had to be modified to incorporate double dispatching (available in all other widget specs). The modified
dispatchTo:with: method sends the message
arbitraryComponent:into: to the
UILookPolicy to build the widget. This message is almost an exact copy of the old
dispatchTo:with: method, with some messages changed to reflect the change of implementing classes.
The above changes enabled us to define the building method for an
ArcbLookPolicy. The method
arbitraryComponent:into: creates the proxy for this widget and registers it with the
ArcbArbitraryComponentProxy class is responsible for communications over the ORB. The most important implementation detail of the
ArcbArbitraryComponentProxy is its update mechanism. When an application is running under ClassicBlend, no
displayOn: messages are sent to its visual components (since the Java widgets define the way they should be displayed locally). Therefore, we need to send the
displayOn: message to the view at the appropriate time. Since the view depends on its application model, we made the
ArcbArbitraryComponentProxy depend on it as well. The proxy sends the
displayOn: message to the view in the
update: method of the
The proxy determines the view it corresponds to in the
updateComponent method. The
ArbitraryComponentSpec has an instance variable, component whose value is a symbol corresponding to the aspect name of the "View Holder" widget. By converting this symbol to a message name and sending it to the application model, we obtain a visual object which understands the
displayOn: message. Here, we refer to that object as a "view", but it doesn't matter where in the VisualPart hierarchy its class actually is, as long as it understands the
displayOn: aGraphicsContext message.
Every time the model changes, we send the
modelChanged method to the Java proxy to prepare the view hierarchy for redisplaying. Then, the
ArcbArbitraryComponentProxy instance sends the
displayOn: message with itself as the parameter. Currently, the most frequently used portion of the
GraphicsContext interface is supported by the proxy. For example, when the view sends a
displayRectangle message to the graphics context (which is an instance of
ArcbArbitraryComponentProxy when the application is running under ClassicBlend), the proxy sends the message
displayRectangle over the ORB. The corresponding Java proxy, upon the receipt of this message, creates a rectangle view and adds it to the main view.
3.2. Java subsystem
The Java portion of our extension to ClassicBlend is briefly illustrated in the diagram below. The source code can be viewed by clicking on the class boxes.
Some source code details have been omitted from the above diagram. Access to all the source code is given in Section 4. Some design patterns occuring in our extension are discussed in Section 3.3.
We have optimized certain parts of the display process in order to avoid redundant displaying of graphical components. If each subcomponent is displayed immediately after it has been created in response to an ORB message from the server, all other subcomponents in the main view whose bounding boxes intersect that of the new one are also redisplayed. This leads to unnecessarily large number of view drawings, which in turn causes the whole view to flicker. To at least partially remedy this, we utilize a delayed display strategy, in which the ORB display message traffic is monitored, and if there have not been any display request for a certain time interval, the entire view (with all its subviews) is displayed. The monitoring object (
ArcbArbitraryViewDraw) runs as a separate thread throughout the life of the main view.
Another thread of control was needed to correctly start the execution of the application (see (
ArcbArbitraryViewUpdate). Its purpose is to ensure that the Java view hierarchy has been initialized before attempting to process ORB display requests.
3.3. Design patterns
The design of our extension to ClassicBlend contains some of the design patterns described in the Design Patterns book by Gamma et al.
ApplicationModelsubclass plays the role of Subject, while the
ArcbArbitraryComponentProxyplays the role of Observer.
GeometricViewclass hierarchy. The
GeometricViewclass serves as the AbstractClass, containing the
drawView(Graphic)abstract method as a primitive operation. The concrete subclasses of
LineView) define the
drawView(Graphics)method, which is called in the
draw()method of their superclass.
ArcbArbitraryComponentProxyserves as the Strategy, the
GraphicsViewserves as the Strategy by declaring a common interface for drawing geometric views, and the concrete classes
RectangleView, etc. serve as ConcreteStrategy classes. Our implementation differs from the Strategy pattern slightly. The
ArcbArbitraryComponentProxyis not configured with one particular strategy; instead it creates new concrete strategies in response to ORB messages from the server.
At present, our extension to ClassicBlend implements only the fundamental graphics context functionality. In order to make our extension fully compatible with any VisualWorks view, the
ArcbArbitraryComponentProxy must include the entire non-private interface of
GraphicsContext. Most methods in the displaying protocol of
GraphicsContext will result in server-client ORB messages when the application is executed under ClassicBlend. To incorporate additional
GraphicsContext methods, follow these steps:
addMethodsToDefinitionclass method of
orb messagesprotocol of
ArcbArbitraryComponentthat has the same name as the
GraphicsContextmethod we are emulating (e.g.
GraphicsContextmessages need not result in an ORB message to the Java display. Such methods usually modify the internal state of the
GraphicsContext, without affecting the display immediately. For example, the
translationmethod simply returns the current value of the instance variable
translation. In general, accessing methods or methods which only affect the internal state of the GraphicsContext (and equivalently,
ArcbArbitraryComponent) are defined in the
spoofingprotocol and need not be included in the
ArcbClassicBlendProxystatic method definitions, e.g.,
Def.define(new GraphicsContextDisplayStringAt(), "displayStringAt", 2);
GeometricViewto handle the display of the new component (e.g.
StringView). You may be able to utilize an existing class from the
makein the top-level ClassicBlend directory.
ArcbArbitraryComponent, modify the
addMethodsToDefinitionclass method and the
com.arscorp.cb.ifc.proxies, modify class
ArcbClassicBlendProxy(add a static definition and a function to execute when the ORB message is received).
com.arscorp.cb.methods.gc, create a class for the new ORB message.
Makefileto include the classes you created.
Please note that there may be exceptions to the above steps. However, in the general case, adding new methods is relatively simple. For debugging your Java code, use the
Debug.println(String) method defined in class
Debug. To turn debugging output on, change the value of the static variable
3.5. Controller issues
We didn't have time to implement propogation of events from the Java side to arbitrary controllers on the Smalltalk side. However, we did figure out what needs to be done to make controllers work properly without specific Java proxies. To understand the changes that need to be made, some background is needed.
3.5.1. How User-Interface Events Work in VisualWorks
There are two loops involved in VisualWorks user events: a polling loop that begins with a
Controller and an event loop that begins with an
The polling loop is initiated by the
Controller. It calls
InputSensor>>pollForActivity, which with a
WindowSensor will call
InputState>>pollForActivity. Contrary to the name and initial appearance of the method,
InputState>>pollForActivity actually waits on a semaphore for an event to change its state. It will only poll for a short time after state has been changed, to catch multiple state changes in quick succession.
InputState>>pollForActivity will return when there is some new data to process.
When there is new data to process, the
Controller asks the
InputSensor (which passes the request on to the
InputState) for mouse positions and other input device information. The
Controller will decide whether or not to act on this new device information with
Controller implements this function to return true if the pointer is in the corresponding
View's display area and the blue button (right button) is not pressed. If
isControlActive returns true, the
Controller polling loop will call
controlActivity and the rest is history.
Before proceeding, the reader should remember that there is one instance of
InputState per image, one
Window, and many
The event loop is in
InputState>>run (instance). It waits for an event to arrive by waiting on a semaphore that it got from its
Screen instance. When the
Screen has an event ready, it will signal the semaphore, and
InputState>>run will retrieve the event using the primitive
Screen will parse the underlying OS/windowing system event structure and produce a VisualWorks event structure. The VisualWorks event structure is an 11-byte array; its definition is in
InputState>>run has an event, it will will call
process: with the event structure as a parameter.
InputSensor child instance that is associated with the
owns the event will receive one of the following event calls:
All of the calls will get the event structure as a parameter. If
InputState>>process:determines if a second click is a double click)
eventEnter(mouse entered region)
eventExit(mouse left region)
eventQuit(close the window)
eventCollapse(the window has been collapsed)
eventExpand(the window has been expanded)
eventWidgetColorChange(platform changed widget color preferences)
InputState>>process:cannot find a window to receive the call, it will look for an
EventDispatcherthat is registered with it and pass the event on.
WindowSensor, the most common
InputSensor child, will queue the window-related events for future action, and pause briefly for events the user would handle to let the polling loop get the changed data.
On the Java side, change
ArcbArbitraryComponentView to handle events. Since
ArcbArbitraryComponentView is a descendent of an IFC
View, there are inherited methods that are called whenever a user input event can be caught by that
View. Our implementation of these event handlers should package the incoming user event into a VisualWorks event structure, and pass the events on through the ORB to the additional
InputState instance on the server side.
Add an additional
WindowSensor child and
InputState child for
ArcbArbitraryComponent to use. The new
InputState will have an new event loop. Instead of waiting for events from the
Screen, it will receive messages from the ORB. It will have one registered function,
receiveEvent: or some such, that will then call
process: on the event. Everything else will follow from that, since every Sensor for "remote" windows gets its data from that one
InputState instance on the VisualWorks side.
The issues that need to be dealt with before this can really be implemented are:
Components actually use the new
InputState. Otherwise, they will get events from the Smalltalk side, not the Java side. Likewise, one would need to make sure that no Windows that correspond to windows displayed on the Smalltalk side are binding to the new InputState instance.
In order to use the code supplied below, there must be a working ClassicBlend installation on your system. This source code has been tested with JDK 1.0.2, VisualWorks 2.5, and ClassicBlend 1.0 on Sun ULTRA-Sparc running Solaris 2.5.1. Both the Java and Smalltalk source files can be downloaded from the source/ directory. This directory also contains a sample Makefile for compiling the Java source. The Smalltalk code is available in both archived and unarchived formats. (Warning: filing in the Smalltalk code will make a minor modification to the UIBuilder, which will not change its functionality unless the UILookPolicy and ArbitraryComponentSpec have been previously changed from their original distribution.)
4.1. Smalltalk source code
stsource.tar.gzarchive (as with the Java code above). This will create a subdirectory
stsource/in the current directory. The
stsource/directory contains all Smalltalk source files.
appletviewer html/demo/dice.html. If you wish to run these applications remotely, modify the html files to include the correct host name.
4.2. Java source code
javasource.tar.gzfile to the top-level ClassicBlend directory.
tar xvf javasource.tar
makein the top-level ClassicBlend directory. Make sure that the CLASSPATH environment variable includes the top ClassicBlend directory.