Creating Components Using Java

The Analysis Server supports many techniques for wrapping analyses. The FileWrapper, PerlWrapper, and ExcelWrapper facilities are the easiest to use. These techniques automate the creation of analysis components for several common types of analyses. For situations where these facilities do not meet user needs, the Java programming language can be used to create custom components. This section describes how to create an analysis component using Java.

This section is merely a good overview and enough for a programmer to get started.  Advanced training materials and classes are available.  Please contact Phoenix Integration about additional material if you wish to get started writing Java Beans for Analysis Server.

NOTE: Using the Java language with the ScriptWrapper utility is often a much easier way to implement custom wrappers. It has almost all the power of a Java Bean, can call to native Java code, and requires no compiler or manifest files.

All components must extend the IPHXAnalysis interface. This interface is a shell data structure (similar to an abstract base class in C++) with required functions for writing a Java class that can be published on the Analysis Server.

The reader should be familiar with programming in Java and must have a Java compiler. Access to the Java "jar" utility is also required. These tools are available free of charge as part of the Java Development Kit (JDK) from www.javasoft.com.

Terminology

The Analysis Server conforms to the Java Beans specification. A Bean is a Java class that is reusable, embeddable, and modular. The Analysis Server can load user-created Java Beans and serve them as analysis components.

A jar file is the distribution mechanism for a Java Bean. One or more class files are archived together into a single jar file. The Analysis Server scans its analyses directories for jar files and publishes the Bean(s) that it finds as components.

A property is an attribute or part of a Bean. For example, an airplane has a weight attribute and is composed of many parts including wings and a fuselage. Weight, wings, and fuselage are all considered properties of an airplane. The term property and variable are often interchanged when describing analysis components.

Java Commands and Syntax

Unlike the wrapping utilities, the creation of custom components through Java Bean programming does not have a special set of commands. The Java language syntax is the only special syntax used. The user should consult a good Java language programming manual when creating custom Java components.

Creating a Component

There are three steps required to create an analysis component:

Step 1: Write and Compile the Desired Java Classes

The first step in creating an analysis component is to write and compile the Java classes that compose the analysis component. Some of these classes will have to access classes provided by the Analysis Server. To link to these files, add the aserver.zip file to your Classpath. The aserver.zip file is located in the directory where the Analysis Server was installed.

Writing an analysis component involves creating code that models some function. For example, a rectangle analysis would calculate the area of the rectangle provided the width and height. The associated Java class would have methods for accessing width, height, and area variables and for performing the calculation for area.

The primary technique for registering variables for the analysis component is to follow the Java Beans paradigm. An object has a readable property x of type Y if it also has a public method named:

public Y getX()

Likewise, it has a writable property x if it has a public method

public void setX( Y value )

In order to include standard Analysis Server descriptive fields, the user must implement specific static methods in the class that return a string. These methods are:

public static String getAuthor()
public static String getDescription()
public static String getVersion()
public static String getHelpURL()
public static String getIconFile()
public static String getKeywords()

These methods correspond to the author, description, version, and date fields of the component. For more information about these descriptive fields, see the describe command description in the Analysis Server portion of this document.

Step 2: Package the Classes as a Jar File

Once the required classes have successfully been compiled they must be packaged in a jar file. To create a jar file, first create a file named manifest.stub. This file should list information about each class packaged in the jar file, whether the class is a Java Bean or not. Only those classes that are Java Beans will be published as Analysis Server components, but the component author may choose to include supporting items, such as utility classes, in the jar file with the Beans. The following lines show the contents of the manifest.stub file used to package two classes called MyClass and HisClass:


Name: MyClass.class
Java-Bean: True

Name: HisClass.class
Java-Bean: False 

Note that a blank line must separate each entry in the manifest.stub file, and the file must begin and end with a blank line.

Once the manifest.stub file is created, the following command packages the classes and Beans into a jar file:

jar -cfm OurJar.jar manifest.stub *.class

This command combines all the class files in the current directory into a jar file named Ourjar.jar. Note that the name of the jar file is arbitrary.

Step 3: Store the .jar File in an Analysis Server analyses Directory

Once the analysis component has been packaged into a jar file, it is ready to be served by the Analysis Server. The jar file should be stored in any directory served by the Analysis Server. This directory will be mapped to an Analysis Server category. Issuing the "lc" command for the specified category should result in a listing of all Beans you have packaged into the jar file. Starting an analysis component will result in the instantiation of your class.

Getting and setting values causes the appropriate get and set methods to be called. Executing the analysis causes the execute() method to be called. Ending an analysis or terminating a connection with the Analysis Server causes the end() method to be called.

A Simple Example

This example shows how to use Java to create a component that computes the area of a rectangle given its height and width. The width and height are two read-write properties (component inputs), while area is a read-only property (component output). All are of type double. Notice that each input property has a get and set method, while the lone output property has a get method only.

In addition to providing access to its properties, analysis components should also create two functions: one for executing the analysis component and one for ending the component. These two functions are both defined by the IPHXAnalysis interface. Each analysis component must implement this interface. The execute and end methods may, and should, throw exceptions when errors occur.

// This Java class computes the area of a rectangle
// based upon a width and a height.
// Includes Java classes that help implement Analysis
// Server components. IPHXAnalysis comes from here.
import com.phoenix_int.aserver.*;
// This is the main class definition. Note that it
// must implement IPHXAnalysis.
public class Rectangle implements IPHXAnalysis
{
  // Private data members of the class.
  private double _width;
  private double _height;
  private double _area;
  // Constructor for the class.
  public Rectangle()
  {
  }
  //---------------------------------------
  // get and set methods for data members.
  public double getWidth()
  {
     return _width;
  }
  public void setWidth( double w )
  {
     _width = w;
  }
  public double getHeight()
  {
     return _height;
  }
  public void setHeight( double v )
  {
     _height = v;
  }
  public double getArea()
  {
     return _area;
  }
  // All Analysis Server components must have an
  // execute method. This one performs a simple
  // calculation.
  public void execute()
  {
     _area = _width*_height;
  }
  // All Analysis Server components must have an
  // end method. This one does nothing when the
  // component instance is terminated.
  public void end()
  {
  }
}

The rectangle example is a simple Java Bean that computes an area based upon a hardcoded equation. The Java Bean framework also supports the wrapping of analysis codes. Say, for example, that the rectangle's area was actually computed by a C program. The author could write Java code that executes the program and parses its outputs to get the area. The code required to perform these tasks can be found in a good Java language programming manual.

In order to publish this class on the Analysis Server, the author must first create a jar file that holds it. In order to do this, he should create a manifest stub file that looks like this:

Name: Rectangle.class
Java-Bean: True

and create the jar file using the following command:

jar -cfm Rectangle.jar manifest.stub Rectangle.class

Note that this jar file will only hold the Rectangle class. The user can specify any number of classes to include or use a wildcard (*) to place all of the classes in Rectangle's directory.

Once Rectangle.jar has been created the user must store it in one of two places: inside the analyses directory or inside a public_aserver directory.

Hierarchical Objects

The Analysis Server also supports more advanced modeling. For example, it is possible to create hierarchies of objects. An airplane object may contain a wing object that contains a span variable. This structure would be implemented by creating an Airplane and Wing class. The Airplane class would have a get method for accessing the wing object and the Wing class would have a get method for accessing the span. The process the Analysis Server uses for setting and getting properties is as follows:

To get the value of a property, the Analysis Server successively calls the appropriate get methods for objects. For example, to issue the command "get airplane.wing.span," the Analysis Server would issue the getWing() method for class Airplane and getSpan() for class Wing.

To set the value of a property, the Analysis Server first retrieves the parent object of the desired property and then calls the appropriate set method for it. For example, to issue the command "set airplane.wing.span = 20," the Analysis Server would issue the getWing() method for class Airplane, setSpan() method for class Wing. If either class Airplane or class Wing do not possess the appropriate set methods, then the property is considered read-only and its value may not be modified.

Self Managers

Sometimes when you are writing a component, you don't know at compile time all of your property names. For example, you may wish to read a dynamic configuration file which decides what type of wing you will be representing. In order to achieve this you need a way to specify at run-time what your properties are. You can accomplish this by extending PHXSimpleSelfManager2 and using the addVariable() method. Below you will find a short example which uses this class.

You may notice that there is a PHXSimpleSelfManager and a PHXSimpleSelfManager2. The difference is that the internal interfaces of the latter are designed to be more efficient with large quantities of data. If you are writing a new class, always use PHXSimpleSelfManager2. Unless you override the getValue() or setValue() methods, existing classes which use PHXSimpleSelfManager may be modified to extend PHXSimpleSelfManager2 without any modifications. If you override getValue() and setValue(), you will need to modify your class to use the newer getValue2() and setValue2() methods before you can override the newer interface.

import com.phoenix_int.aserver.*;
import com.phoenix_int.aserver.types.*;
import com.phoenix_int.aserver.manager.*;

public class Rectangle2 extends
PHXSimpleSelfManager2 implements IPHXAnalysis
{
private PHXDouble _width = new PHXDouble();
private PHXDouble _height = new PHXDouble();
private PHXDouble _area = new PHXDouble();

public Rectangle2() throws PHXInvalidNameException,
PHXNameAlreadyInUseException
{
// tell the super class about the variables we want to expose to
// the Analysis Server:
//
// addVariable( varName, isInput?, varObject )
//
super.addVariable( "width", true, _width );
super.addVariable( "height", true, _height );
super.addVariable( "area", false, _area );
}

public void execute()
{
_area.setValue( _width.getValue()*_height.getValue() );
}

public void end()
{
}
}

Interfaces

There are a handful of other interfaces that you can implement as a Java Bean which allow you to add other functionality to your component. See the associated documentation for these interfaces if you need the functionality.

nD Arrays

Analysis Server contains special classes to make handling multi-dimensional arrays easier.  These classes can all be found in the package com.phoenix_int.aserver.types, just like PHXDouble and PHXString, and are named PHXDoubleArray,PHXStringArray, etc. 

When you use these classes for arrays, Analysis Server automatically allows input arrays to be resized from the client, both in number of dimensions and size of the dimensions.  It is up to the component writer to either lock down the array sizes with the "lockDimensions" and "lockResize" properties, or to check the input array sizes to make sure they are valid.

You can still use traditional Java Arrays as inputs and outputs of your component.  They have limited functionality compared to the PHX<type>Array classes, however.

Example nD Array Java Bean

See also Analysis Server