Application Development


Chapter 21 . Java Event Handlers and Objects

You can use the Java programming language as an alternative to JPL or C by writing Java event handlers for the screens, service components and widgets in your Panther applications. Panther 5.40 also has enhanced support for working with Java objects.

Support for the Java language has been implemented differently than Panther's C language support. Unlike C functions, you cannot enter the name of a Java method in widget or screen event properties. With Java, you assign a Java class to the Panther object (screen, service component, widget) in the Java Tag property. This Java class will serve as the event handler for the object.


Java Overview

Java is an object-oriented programming language. Unlike C, in which you write functions to manipulate data stored in separate structures, Java objects are a combination of data and methods (functions) that manipulate that data. The data and methods define a class for the objects that can be constructed from that class. A class is not an object, but is a blueprint for an object. At runtime, a class factory creates, or instantiates, instances of the object from the class definition.

Classes are arranged in a hierarchy, which allows a subclass to inherit from a parent class, or superclass. A set of classes is called a package.


Using Java in Panther

By default, Java support is enabled in Panther as determined by the behavior variable JAVA_USE. When this variable is set to JAVA_IN_USE, on startup, Panther dynamically loads a library to initialize Java support. If the library cannot be found, Panther reports an error that Java support is not enabled.

Since the location of the Java library can vary on different platforms, you can override the default location by setting the variable SMJAVALIBRARY to the correct location in your environment or in an initialization file.

Writing Java Code

Within the Panther editor, options on the File menu allow you to open, create and save Java files. You can use the text editor specified by SMEDITOR or specify a special Java editor with SMJAVAEDITOR.

You can compile Java programs in the editor using ToolsCompile Java. The compilation command can be specified by setting SMJAVACOMPILE.

Determining the Java Event Handler

Screens, service components and widgets in Panther have a Java Tag property, which can be found under Identity in the Properties window. This property identifies the Java class that is to serve as the event handler for that screen, service component, or widget. At runtime, the Java Tag is passed to a function in the class specified by SMJAVAFACTORY, which then provides the event handler itself.

The default SMJAVAFACTORY is ClassTagFactory.java, which simply assumes that the Java tags are the names of classes. If you want to write another method for matching the Java tags to classes, you can write a your own class factory and specify it using SMJAVAFACTORY.

Figure 21-1 The Java Tag property identifies the Java class that is the event handler.

The event handler classes must provide methods that correspond to the various kinds of events supported by the object (screen, service component or widget) that it is associated with. To this end, predefined interfaces have been provided for the event handler classes to implement. If a class that does not implement the appropriate interface is named as the event handler for a given object, an error will be returned.

At runtime, when an event occurs on an object that has a Java tag set, Panther will send a message to the class that is serving as the object's event handler. The event handler will then perform the action that is programmed for that event. This method will be passed a series of parameters when invoked. Refer to the next section for documentation of each event and a list of the parameters it will receive.

If there is a JPL procedure or C function also associated with the event (in the Properties window), that procedure will be called after the Java event handler has returned. In this way one can associate both methods written in Java and procedures written in C or JPL with the same events, though the Java methods will always be invoked first.

Panther appends the location of its classes ($SMBASE/config/pro5.jar) to the CLASSPATH environment variable at runtime. As part of your application setup, you must also specify the location of your Java classes in that variable.


Event Handler Interfaces

There are interfaces defined for screens and for the various widget types. In addition to these interface definitions, adapter classes have been provided that provide null implementations of these interfaces. These are located in the distribution at $SMBASE/config/pro5.jar. The HTML listing of the interfaces in Javadoc format is at $SMBASE/config/java, along with the source class files.

To implement an event handler for a given screen or widget, you would typically subclass the appropriate adapter class, and locally redefine those methods that you wish the screen or widget to support in a non-null manner. For an example, refer to "Java Samples."

Table 21-1 The interface and adapter class files for widgets and screens

Object Interface file Adapter class

ActiveX controls

ActivexHandler.java

ActivexHandlerAdapter.java

Check boxes

CheckboxHandler.java

CheckboxHandlerAdapter.java

Combo boxes

ComboboxHandler.java

ComboboxHandlerAdapter.java

Dynamic labels

DynamicLabelHandler.java

DynamicLabelHandlerAdapter.java

Grids

GridHandler.java

GridHandlerAdapter.java

Groups

GroupHandler.java

GroupHandlerAdapter.java

List boxes

ListboxHandler.java

ListboxHandlerAdapter.java

Option menus

OptionmenuHandler.java

OptionmenuHandlerAdapter.java

Push buttons

ButtonHandler.java

ButtonHandlerAdapter.java

Radio buttons

RadiobuttonHandler.java

RadiobuttonHandlerAdapter.java

Scales

ScaleHandler.java

ScaleHandlerAdapter.java

Screens

ScreenHandler.java

ScreenHandlerAdapter.java

Tab cards

TabCardHandler.java

TabCardHandlerAdapter.java

Text fields

TextHandler.java

TextHandlerAdapter.java

Toggle buttons

TogglebuttonHandler.java

TogglebuttonHandlerAdapter.java

Screen Event Handlers

An event handler for a screen must implement the ScreenHandler interface, either explicitly or by subclassing ScreenHandlerAdapter. As such it must support the following methods:

screenEntry
void screenEntry(ScreenInterfaces, int context);

screenExit
void screenExit(ScreenInterfaces, int context);

screenKey
void screenKey(ScreenInterfaces, int key);

screenWebEnter
void screenWebEnter(ScreenInterfaces);

screenWebExit
void screenWebExit(ScreenInterfaces);

The first parameter passed to each of these methods is a handle to the screen itself. The second parameter passed to the methods screenEntry and screenExit is an integer bitmask containing the context flags for the event. The second parameter passed to the method screenKey is an integer that corresponds to a logical key. This method is used to respond to logical keys, such as the PF-keys (typically pressed by user action) or APP-keys (typically pressed programmatically).

The methods screenWebEnter and screenWebExit do not receive a second parameter.

ActiveX Control Event Handlers

Event handlers for ActiveX controls only handle the events for the Panther containers used to house the controls. Any events supported by the controls themselves will be implemented by the controls; use sm_com_set_handler in the CFunctionsInterface to specify event handlers for those events.

An event handler for an ActiveX control must implement the ActiveXHandler interface, either explicitly, or by subclassing ActiveXHandlerAdapter. As such it must implement the following methods:

activexEntry
int activexEntry(FieldInterface f, int item, int context);

activexExit
int activexExit(FieldInterface f, int item, int context);

activexValidate
int activexValidate(FieldInterface f, int item, int context);

Each of these methods receives three parameters. The first is a handle to the ActiveX control itself. The second is an integer containing the control's occurrence number. The third is an integer bitmask containing the context flags for the event.

Check Box Event Handlers

An event handler for a check box must implement the CheckboxHandler interface, either explicitly or by subclassing CheckboxHandlerAdapter. As such it must support the following methods:

checkboxEntry
int checkboxEntry(FieldInterface f, int item, int context);

checkboxExit
int checkboxExit(FieldInterface f, int item, int context);

checkboxValidate
int checkboxValidate(FieldInterface f, int item, int context);

Each of these methods receives three parameters. The first is a handle to the check box itself. The second is an integer corresponding to the check box's occurrence number. The third is an integer bitmask containing the context flags for the event.

Combo Box Event Handlers

An event handler for a combo box must implement the ComboboxHandler interface, either explicitly or by subclassing ComboboxHandlerAdapter. As such it must support the following methods:

comboboxDoubleClick
int comboboxDoubleClick(FieldInterface f, int item);comboboxEntryint

comboboxEntry
(FieldInterface f, int item, int context);comboboxExit
int comboboxExit
(FieldInterface f, int item, int context);
comboboxValidate
int comboboxValidate(FieldInterface f, int item, int context);

The first two parameters received by each of these methods are a handle to the combo box itself, and an integer corresponding to the combo box's occurrence number. The method comboboxDoubleClick receives only these two parameters; the methods comboboxEntry, comboboxExit and comboboxValidate also receive a third parameter, which is an integer bitmask containing the context flags for the event.

Dynamic Label Event Handlers

An event handler for a dynamic label must implement the DynamicLabelHandler interface, either explicitly or by subclassing DynamicLabelHandlerAdapter. As such it must support the following methods:

labelDoubleclick
int labelDoubleClick(FieldInterface f, int item);

labelValidate
int labelValidate(FieldInterface f, int item, int context);

The first two parameters received by each of these methods are a handle to the dynamic label itself and an integer corresponding to the dynamic label's occurrence number. The method labelDoubleClick receives only these two parameters; the method labelValidate also receives a third parameter, which is an integer bitmask containing the context flags for the event.

Grid Event Handlers

An event handler for a grid widget must implement the GridHandler interface, either explicitly or by subclassing GridHandlerAdapter. As such it must support the following methods:

gridEntry
int gridEntry(GridInterface g, FieldInterface f, int item, int context);

gridExit
int gridExit(GridInterface g, FieldInterface f, int item, int context);

gridValidate
int gridValidate(GridInterface g, FieldInterface f, int item, int context);

gridRowEntry
int gridRowEntry(GridInterface g, FieldInterface f, int item, int context);

gridRowExit
int gridRowExit(GridInterface g, FieldInterface f, int item, int context);

The GridHandler methods gridEntry, gridExit and gridValidate are passed four parameters. The first is a handle to the grid itself. The second is a handle to the grid member (field) that currently has focus. The third is an integer corresponding to the grid member's occurrence number. The fourth is an integer bitmask containing the context flags for the event. The methods gridRowEntry and gridRowExit receive five parameters. The first is a handle to the grid itself. The second is an integer, corresponding to the row number. The third is a handle to the grid member that is gaining or losing focus. The fourth is an integer corresponding to the occurrence number for the grid member that is gaining or losing focus. The fifth is an integer bitmask containing the context flags for the event.

Group Event Handlers

An event handler for a group must implement the GroupHandler interface, either explicitly or by subclassing GroupHandlerAdapter. As such it must support the following methods:

groupEntry
int groupEntry(GroupInterface g, int context);

groupExit
int groupExit(GroupInterface g, int context);

groupValidate
int groupValidate(GroupInterface g, int context);

Each of these methods receives two parameters. The first is a handle to the group itself. The second is an integer bitmask containing the context flags for the event.

List Box Event Handlers

An event handler for a list box must implement the ListboxHandler interface, either explicitly or by subclassing ListboxHandlerAdapter. As such it must support the following methods:

listboxActivate
int listboxActivate(FieldInterface f, int item);

listboxDoubleClick
int listboxDoubleClick(FieldInterface f, int item);

listboxEntry
int listboxEntry(FieldInterface f, int item, int context);

listboxExit
int listboxExit(FieldInterface f, int item, int context);

listboxValidate
int listboxValidate(FieldInterface f, int item, int context);

The first two parameters received by each of these methods are a handle to the list box itself and an integer corresponding to the list box's occurrence number. The methods listboxDoubleClick and listboxActivate receive only these two parameters; the methods listboxEntry, listboxExit and listboxValidate also receive a third parameter, which is an integer bitmask containing the context flags for the event.

Option Menu Event Handlers

An event handler for an option menu must implement the OptionmenuHandler interface, either explicitly or by subclassing OptionmenuHandlerAdapter. As such it must support the following methods:

optionmenuEntry
int optionmenuEntry(FieldInterface f, int item, int context);

optionmenuExit
int optionmenuExit(FieldInterface f, int item, int context);

optionmenuValidate
int optionmenuExit(FieldInterface f, int item, int context);

Each of these methods receives three parameters. The first is a handle to the option menu itself. The second is an integer corresponding to the option menu's occurrence number. The third is an integer bitmask containing the context flags for the event.

Push Button Event Handlers

An event handler for a push button must implement the ButtonHandler interface, either explicitly or by subclassing ButtonHandlerAdapter. As such it must support the following methods:

buttonActivate
int buttonActivate(FieldInterface f, int item);

buttonEntry
int buttonEntry(FieldInterface f, int item, int context);

buttonExit
int buttonExit(FieldInterface f, int item, int context);

buttonValidate
int buttonValidate(FieldInterface f, int item, int context);

The first parameter passed to each of these methods is a handle to the push button itself. The second parameter is an integer corresponding to the occurrence number of the push button. The buttonActivate method receives only these two parameters; the methods buttonEntry, buttonExit, and buttonValidate also receive a third parameter that is an integer bitmask containing the context flags for the event. Note that the buttonActivate event is invoked when the button is clicked on. It corresponds to the hook accessed in the Properties window by means of the Control String property.

Radio Button Event Handlers

An event handler for a radio button must implement the RadiobuttonHandler interface, either explicitly or by subclassing RadiobuttonHandlerAdapter. As such it must support the following methods:

radiobuttonEntry
int radiobuttonEntry(FieldInterface f, int item, int context);

radiobuttonExit
int radiobuttonExit(FieldInterface f, int item, int context);

radiobuttonValidate
int radiobuttonValidate(FieldInterface f, int item, int context);

Each of these methods receives three parameters. The first is a handle to the radio button itself. The second is an integer corresponding to the radio button's occurrence number. The third is an integer bitmask containing the context flags for the event.

Scale Event Handlers

An event handler for a scale must implement the ScaleHandler interface, either explicitly or by subclassing ScaleHandlerAdapter. As such it must support the following methods:

scaleEntry
int scaleEntry(FieldInterface f, int item, int context);

scaleExit
int scaleExit(FieldInterface f, int item, int context);

scaleValidate
int scaleValidate(FieldInterface f, int item, int context);

Each of these methods receives three parameters. The first is a handle to the scale itself. The second is an integer corresponding to the scale's occurrence number. The third is an integer bitmask containing the context flags for the event.

Tab Card Event Handlers

An event handler for a tab card must implement the TabCardHandler interface, either explicitly or by subclassing TabCardHandlerAdapter. As such it must support the following methods:

cardCardEntry
void cardCardEntry(FieldInterface f, int context);

cardCardExit
void cardCardExit(FieldInterface f, int context);

cardExpose
void cardExpose(FieldInterface f, int context);

cardHide
void cardHide(FieldInterface f, int context);

cardTabEntry
int cardTabEntry(FieldInterface f, int item, int context);

cardTabExit
int cardTabExit(FieldInterface f, int item, int context);

cardValidate
int cardValidate(FieldInterface f, int item, int context);

The first parameter passed to each of these methods is a handle to the tab card itself. The methods cardTabEntry, cardTabExit and cardTabValidate receive as a second parameter an integer corresponding to the tab cards card_number property, and as a third parameter an integer bitmask containing the context flags for the event. The methods cardCardEntry, cardCardExit, cardExpose and cardHide receive only two arguments, the second being an integer bitmask containing the context flags for the event.

Toggle Button Event Handlers

An event handler for a toggle button must implement the TogglebuttonHandler interface, either explicitly or by subclassing TogglebuttonHandlerAdapter. As such it must support the following methods:

togglebuttonEntry
int togglebuttonEntry(FieldInterface f, int item, int context);

togglebuttonExit
int togglebuttonExit(FieldInterface f, int item, int context);

togglebuttonValidate
int togglebuttonValidate(FieldInterface f, int item, int context)

Each of these methods receives three parameters. The first is a handle to the toggle button itself. The second is an integer corresponding to the toggle button's occurrence number. The third is an integer bitmask containing the context flags for the event.

Text Field Event Handlers

An event handler for a text field (either single-line or multi-line) must implement the TextHandler interface, either explicitly or by subclassing TextHandlerAdapter. As such it must support the following methods:

textDoubleClick
int textDoubleClick(FieldInterface f, int item);

textEntry
int textEntry(FieldInterface f, int item, int context);

textExit
int textExit(FieldInterface f, int item, int context);

textValidate
int textValidate(FieldInterface f, int item, int context);

The first two parameters passed to each of these methods are a handle to the text field itself, and an integer corresponding to the text field's occurrence number. The method textDoubleClick receives only these two parameters; the methods textEntry, textExit and textValidate also receive a third parameter, which is an integer bitmask containing the context flags for the event.


Object Interfaces

The object handles passed to the event handler methods are handles to Java objects that correspond to the Panther objects on the screen. These objects themselves support a variety of methods, and the event handlers that you write will typically make use of these methods.

The interfaces supported by these objects are defined in the files:

ScreenInterface.java
FieldInterface.java
GridInterface.java
GroupInterface.java

Each of these interfaces extends WidgetInterface, which is defined in:

WidgetInterface.java

Therefore the methods in WidgetInterface are common to all the objects that correspond to Panther objects.

The objects corresponding to text fields, push buttons, toggle buttons, check boxes, radio buttons, dynamic labels, tab cards, option menus, combo boxes, listboxes, and scales are all of type FieldInterface, and hence support all the methods defined in FieldInterface.java and WidgetInterface.java.

In addition to the objects that corresponds to the various widgets and screens, there is also an Application object that supports a few methods. The interface for the Application object is defined in ApplicationInterface.java.


Implementing Service Component Methods in Java

In general the event handlers for service components are like those of screens. But service components have to respond to a special class of events that the Screen Handler interface doesn't predefine. Service components have to respond to client-initiated requests for methods.

The methods supported by a component are named functions. In COM components or Enterprise JavaBeans, the method names are specified in the editor, in the Component Interface window. In JetNet or Oracle Tuxedo the procedure names are associated with component names in the JIF; each component/procedure pair corresponds to a "service" as far as the middleware is concerned.

At runtime, if a service component has its Java tag property set, the server will attempt to invoke a method with a name corresponding to the name of the method invoked by the client (the name specified either in the Component Interface View or the JIF). Hence if a server component is to implement methods in Java, its event handler must implement methods with names that correspond to the component's public methods. If a Java method with a name corresponding to the method name is found, the Panther server will not continue to look for JPL or C functions with the same name. (This behavior differs from event calls for screen and widget events.) When methods are invoked in this fashion, they will receive two parameters. The first is a handle to the service component itself, this is an object of type ScreenInterface. The second is a handle to an interface that supports the middleware-specific functions needed to implement a service.

Service Component Methods in Oracle Tuxedo and JetNet

The methods of JetNet or Oracle Tuxedo service component handlers receive as their second parameter an object that implements TPFunctionsInterface. Such an object supports the following methods:

int sm_tp_exec(String a1);
WidgetInterface getTpRequest();
WidgetInterface getTpRequest(String callid);

The method getTpRequest returns a handle to an object that represents a service request. These objects implement WidgetInterface. Interactions with such an object will generally only be for the purpose of querying its property values. The method sm_tp_exec corresponds to the Panther library function of the same name.

Service Component Methods in COM/DCOM/MTS

The methods for service components in COM applications receive as their second parameter a handle to an object that implements ComFunctionsInterface. Such an object supports the following methods:

int receive_args (String text);
int return_args (String text);
int raise_exception (int code);
int log (String text, int code);
int sm_mts_CreateInstance (String text);
int sm_mts_SetComplete ();
int sm_mts_SetAbort ();
int sm_mts_EnableCommit ();
int sm_mts_DisableCommit ();
int sm_mts_IsInTransaction ();
int sm_mts_IsSecurityEnabled ();
int sm_mts_IsCallerInRole (String role);

The functions receive_args, return_args, raise_exception and log correspond to the JPL verbs of those names. The rest of the methods correspond to the Panther library functions of the same names.

Service Component Methods in WebSphere

The methods of a service component and Enterprise JavaBean deployed in WebSphere Application Server receive as their second parameter an object that implements WSFunctionsInterface. Such an object supports the following methods:

public PantherSessionBean get_bean();
public int log (String message);
public void raise_exception (String message);
public int receive_args (String args);
public int return_args (String args);

These methods correspond to the JPL verbs with the same names.


Working with Java Objects

Instantiating Java Objects

Starting with Panther 5.40, there is an alternative to using Java event handlers to integrate Java with your Panther application. Perhaps you want to embed a J2SE class, such as a HashMap, into your JPL code. You can do this by making use of the Panther Component subsystem. Although there aren't any non-EJB Java-based Panther Components, the client API for working with Panther Components allows it to work with ordinary Java classes. To do this you set the current_component_system property to PV_SERVER_JAVA before calling sm_obj_create. If your application also uses COM or EJB support, you can change this property as needed.

To access a Java class, you must create an object of the class by calling sm_obj_create. This function takes one or more parameters. The first parameter is a string containing the fully qualified class name. This string may also contain a type-specifier that describes the types of any arguments that are needed by the constructor if the constructor is overloaded. If there are arguments needed for the constructor, they are supplies as additional parameters to the sm_obj_create call. For example, this is JPL code to create a HashMap using the default no-arg constructor:

    vars hashmap_id = sm_obj_create("java.util.HashMap")

Another example, using the HashMap constructor that takes an int for the initial capacity:

    vars hashmap_id = sm_obj_create("java.util.HashMap(int)", 100)

Type-Specifiers and Arguments

The names passed to sm_obj_create and sm_obj_call can indicate the types of the parameters to ensure that the correct method is used. The syntax is:

    <classname-or-methodname>(type[, type] ...)

Type-specifiers may follow the class name passed to sm_obj_create and the method name passed to sm_obj_call. When used, they are included in the class name or method name. Type-specifiers do not conform to Java conventions, but have a custom syntax. The type-specifier is a comma separated list of types, enclosed in parentheses. White space is ignored between the class name or method name and the type-specifier, and between tokens within the parentheses. Types may be primitive or fully qualified class names. There is an exception. For classes in the java.lang package, such as String, the package name may be left off. The primitive type names supported are: boolean, byte, char, double, float, int, long, and short.

Arrays may be used in type-specifiers. To indicate an array, the type name, primitive or class name, must be followed by square brackets containing the size of the array. For example int[5] represents an int array of size 5. The argument corresponding to this type must, therefore, be an array field or JPL array variable containing at least 5 occurrences. Multidimensional arrays are not supported.

When a class name is used as a type, the argument may be a Panther constant, variable, or field name only if the value of the constant, or content of the field or variable can be converted into a Java object of the specified type. For example, Panther will automatically convert a primitive into its wrapper type (for example, int to java.lang.Integer) and a Panther value will be converted into a java.lang.String if needed. For other types, use the @obj() syntax to refer to an existing Java object by its Panther object ID. For example,

    vars newid = sm_obj_create( \
"java.util.HashMap(java.util.Map)", @obj(hashmap_id))

The return value from sm_obj_create is a Panther Object ID on success, or PR_E_OBJID or PR_E_OBJECT on error. A Java global reference is created for the instantiated Object, so that the instance is not garbage collected by the JVM until sm_obj_delete_id is called for the Object ID.

The use of the Panther Object ID for a Java Object is limited to Panther's sm_obj_xxx family of functions for accessing the object's properties and methods.

Destroying Java Objects

After creating and working with the methods and properties of a Java Object, you should destroy it by calling sm_obj_delete_id. This function takes one parameter, the object ID for the object to destroy. If you don't call this function, the instance will continue to exist until the application terminates, even if the application goes from test to edit mode. In other words, the object will not be garbage collected by the JVM until sm_obj_delete_id is called.

For example, sm_obj_delete_id can be called to delete the HashMap created in the previous code as follows:

call sm_obj_delete_id(hashmap_id)

Calling Java Object Methods

To access a Java object's methods, you need to call the function sm_obj_call. The syntax in JPL is:

ret = sm_obj_call (objid, methodName(OptionalTypeSpecifier) \
[, parm] ...)

The first parameter to sm_obj_call is the Panther object ID of the Java object whose method you wish to use. The second parameter is the name of the method you are calling. A type-specifier may be used within this name. The type-specifier is needed if the method is overloaded. The remaining parameters are a comma-separated list of the parameters to the method itself. Field names, variable names, constants, and @obj() may be used, exactly as described above in the section Instantiating Java Objects.

If a type-specifier is not used, Panther will try to locate a method in the class with the provided name and number of arguments and will attempt to convert the parm arguments to the parameter types needed for the method that was located.

If a method called by sm_obj_call returns a String or a value that has a primitive type, Panther will return that value. If the method returns a Java object, a Panther object ID will be returned by sm_obj_call. Such object IDs can be used in the same way as those returned by sm_obj_create. They can be passed in as the first argument of calls to sm_obj_call, and must be deleted by calling sm_obj_delete_id when they are no longer needed.

Below is JPL code demonstrating the concepts discussed so far:

// Initialize the component system to for Java
@app()->current_component_system = PV_SERVER_JAVA

vars a = 1234567891

/* Create a BigInteger using the constructor that takes a
* String argument. Since JPL can treat the content of
* 'a' as a String if it is used as such, there is no
* potential loss of preceision.
*/
vars bigInteger1 = sm_obj_create("java.math.BigInteger(String)",a)
/* Call the 'multiply' method.In this case the 'pow'
* method could have been used instead. If 'multiply' were
* overloaded, we could use multiply(java.math.BigInteger)
* as the second argument below.
*/
vars bigInteger2 = sm_obj_call(bigInteger1, \
"multiply", @obj(bigInteger1))
// Convert the result to a string.
vars b = sm_obj_call(bigInteger2, "toString")
// Delete the Object IDs to allow JVM garbage collection.
call sm_obj_delete_id(bigInteger1)
call sm_obj_delete_id(bigInteger2)
msg emsg b           // Displays 1524157877488187881

Accessing Panther Functions From a Java Method

The class com.prolifics.jni.Application contains the static method getInstance. This method returns an ApplicationInterface for the the Application Object. Using this instance, one can call getCFunctions, getDMFunctions, getRWFunctions and getTMFunctions. For example, here is Java code showing this technique:

    package com.prolifics.samples;
import com.prolifics.jni.*
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
    public class LdapAuthentication
{
DirContext ctx = null;
        public int authenticate() {
int ret = -1;
ApplicationInterface ai = Application.getInstance();
CFunctionsInterface cf = ai.getCFunctions();
            String ldapURL  = c.sm_n_fptr("ldap_url");
String username = c.sm_n_fptr("username");
String password = c.sm_n_fptr("password");
            try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapURL);
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
ctx = new InitialDirContext(env);
ret = 0;
} catch (Exception e) {
c.sm_message_box(e.toString(),
"Authentication Failure", 0, "");
}
return ret;
}
}

The above Java code above can be used from JPL as follows:

    vars ldapAuth = sm_obj_create(
"com.prolifics.samples.LdapAuthentication")
vars ret = sm_obj_call(ldapAuth, "authenticate")
call sm_obj_delete_id(ldapAuth)
if (ret == 0)
msg emsg "Login Successful"

The code shown above is not the best integration of Java with JPL, nor does it handle exceptions very well. It is designed only for demonstrating the techniques discussed in this section.

Panther Library Functions are generally not thread safe. Therefore, avoid creating threads within Java code with the potential to invoke Panther library functions simultaneously from different threads.

Accessing Java Object Properties

Properties of Java objects are class member variables, also called fields. It is recommended with Java Beans that you use the sm_obj_call function with setter and getter methods. If this is not an option, you can use the sm_obj_set_property and sm_obj_get_property functions for this purpose. The following example sets a property on the Java object associated with the id variable:

ret = sm_obj_set_property(id, "PropName", "PropSetting")

A type-specifier cannot be used with sm_obj_set_property, and the property type may only be a primitive, a primitive wrapper type, or a String. Arrays are not supported. Also, @obj() is not supported for the third argument of sm_obj_set_property, nor can sm_obj_get_property return an object ID.

Designating an Error Handler

You can define an error handler for Java method invocations. For example:

call sm_obj_onerror("ErrorHandlerName")

The string passed to sm_obj_onerror is the name of the function that you want to use as the error handler. This may be the name of a JPL function, a C prototyped function, or a C control function. If a Java operation (method call, property access, or object creation) fails for some reason, including if a Java method throws an exception, the error handler function will be called. This function is passed three parameters: the property value of the current_component_system property - for Java it is PV_SERVER_JAVA, the message number, in decimal format, of a message from the Panther message file for the error, and the corresponding message describing the error. In the case of a C control function, the three parameters are concatenated into one string parameter. The handler does not receive the Java exception string nor the Java stack trace. It only receives a message that there was an unhandled Java exception. It may, therefore, be advisable to cache Java exceptions in a Java object that has methods for retrieving the exception string and stack trace.


Java Samples

The Java methods in these samples are taken from a Panther-built calculator program. The calculator contains a screen event handler for the calculator window and several classes of button event handlers for the push buttons which perform the calculator's operations. The calculator is located in the distribution at $SMBASE/samples/javacalc; refer to Appendix C, "Panther Java Calculator," for development notes.

The screen event handler initializes global JPL variables and reprograms some keys. The Java Tag for the screen is CalcScreen.

import com.prolifics.jni.*;
// This class provides the Screen Entry and Screen Exit
// functions for the Calculator.
// It initializes JPL globals and re-programs
// the BS,DELE and ENTER keys.
public class CalcScreen
extends ScreenHandlerAdapter
{
public void screenEntry(ScreenInterface si, int context)
{
CFunctionsInterface c = si.getCFunctions();
// Set JPL global variables 
c.sm_n_putfield("op_just_done","1");
c.sm_n_putfield("operation","");
c.sm_n_putfield("memory","0");
c.sm_n_putfield("register","0");
c.sm_n_putfield("degrees","0");
c.sm_n_putfield("currency","0");
// Set <Enter> to generate '=' Key press,
// Backspace to generate "<",
// and Delete to Generate "E"(clear Entry)
c.sm_keyoption(Keys.NL, KEY_XLATE, '=');
c.sm_keyoption(Keys.BKSP, KEY_XLATE, '<');
c.sm_keyoption(Keys.DELE, KEY_XLATE, 'E');
}
	public void screenExit(ScreenInterface si, int context
{
CFunctionsInterface c = si.getCFunctions();
// Restore default functions of Backspace, Delete and Enter
c.sm_keyoption(Keys.NL, KEY_XLATE, Keys.NL);
c.sm_keyoption(Keys.BKSP, KEY_XLATE, Keys.BKSP);
c.sm_keyoption(Keys.DELE, KEY_XLATE, Keys.DELE);
}
}

One of the button event handlers is for the push buttons that implement the memory functions of the calculator. The push buttons using this event handler have a Java Tag of CalcMem.

import com.prolifics.jni.*;

// This class provides A Handler for all calculator
// operations which implement the memory operations.
// There is an if-else-if block which implements
// the individual functions based on the button labels.
public class CalcMem
extends ButtonHandlerAdapter
{
public int buttonActivate(FieldInterface fi,int item)
{
ScreenInterface si = fi.getScreen();
CFunctionsInterface c = si.getCFunctions();

// Save Handle to display since it is used a few times
FieldInterface fiDisplay = si.getField("display");
// Get value of Display widget and Key Pressed
String key = fi.getfield();
double dDisplay = fiDisplay.dblval();
// Get values from JPL global vars
double mem = c.sm_n_dblval("memory");
		if (key.equalsIgnoreCase("MC"))
c.sm_n_putfield("memory","0");
		else if (key.equalsIgnoreCase("MR"))
{
fiDisplay.putfield(String.valueOf(mem));
fiDisplay.fval();
}
		else if (key.equalsIgnoreCase("M+"))
{
mem += dDisplay;
c.sm_n_putfield("memory",String.valueOf(mem));
}
c.sm_n_putfield("op_just_done","1");
return 0;
}
}