By David Wallwork
Two Modes Of Operation
In the first few lines of code, widget.bbj looks for a command line parameter 'debug' and sets a flag usingBridge. The
purpose of this is to allow us to test widget.bbj without using a bridge. When run using the command "bbj
widget.bbj - debug" our program will read input from a local file rather than from the bridge channel. This allows the
program to be fully tested before introducing the added complexity of running within a bridge. It is very important
that a program that is intended to be called from a bridge should be tested as fully as possible before actually
calling it from within Java code since the process of debugging becomes more difficult as we add more 'layers' on top
of it.
The "Jxx" Device Type
Just as a device whose name that begins with 'P' always resolves to a printer in BBj, a device whose name begins with
'J' always resolves to a Java plug-in. In our example "J0" is defined in our config file by the line:
ALIAS J0 com.basis.bbj.bridge.BBjBridgeOpenPlugin
Although a Java plug-in can be provided by the user or by a third party, this particular plug-in is provided by BASIS
for the purpose of communicating between Java code and BBj code. Once a channel has been opened with this ALIAS, we can
READ data that was placed in the BBjBridgeRequest by the Java code. Any data that we WRITE to that channel will become
available to the Java code through the BBjBridgeResponse. For more information see the BASIS Online Documentation.
Calling RELEASE In The BBj Program
The Java program, WidgetBridge.java, can obtain the exit code of widget.bbj in order to determine whether widget.bbj
executed successfully. In our example, the Java program and the BBj program have agreed on the meaning of several exit
codes. This allows the Java program to provide more meaningful information to the caller in the case that the BBj
program fails.
User Interaction When usingBridge Is True
Our goal is to reuse WidgetBridge within a Servlet that will be run on a web server. Since a Servlet has no user
interface, we must write widget.bbj in such a way that it will execute without user interaction. So normally,
widget.bbj does not drop to console or execute any input verbs. But even if widget.bbj has been well tested, there may
be problems in the calling Java code that will cause widget.bbj to fail. If, for example, the calling program has not
used the correct key values when writing data to the BBjBridgeRequest, then widget.bbj will fail when it attempts to
read the data. Such errors can be difficult to find if we are not able to step through the BBj code while it is being
called from the bridge.
We resolve this dilemma by testing fid(0) within widget.bbj. By default, a program that is being executed through a call
to Java BBjBridge will have terminal type -tIO. The calling Java program can set a different terminal type by modifying
the String that is passed to BBjBridgeRequest.setCommandLine(). In widget.bbj we test to see if the terminal type has
been set and if it has then we drop to console. This is a convenient way to allow dot stepping through widget.bbj even
while it is being run through Java BBjBridge.
WidgetBridge.java
The code for WidgetBridge.java can be found in Listing B. The main() method in WidgetBridge constructs a WidgetBridge and
passes its own command line parameters to widget.bbj. The results of running widget.bbj are printed to System.out and
WidgetBridge.main() exits. This main() method may be executed from the O/S command prompt by commands similar to one of
the following:
java WidgetBridge 123 456 789
OR
java WidgetBridge 123 456 789 -tT0
Although WidgetBridge.java demonstrates how we can use a Java BBjBridge, it is not very efficient. Each time we run
WidgetBridge.main() we must:
- start a new Java session
- create a new instance of WidgetBridge
- create a new instance of Java BBjBridge
- establish a new connection to BBjServices
On a reasonably fast machine this will typically take several seconds. In the next section we will examine two
programs that reduce this time to the range of 20-40 milliseconds.
Listing B - WidgetBridge.java (Right click and Save to download code)
import java.io.*;
import java.text.*;
import java.util.*;
import com.basis.bbj.bridge.*;
public class WidgetBridge{
// define timeout and expected return codes
private final static int BRIDGE_TIMEOUT = -1;
private final static int READ_ERROR = -1;
private final static int WRITE_ERROR = -2;
private final static int UNKNOWN_ERROR = -3;
// define configuration file and program file
private static String CONFIG_FILE = "c:/basis/work/config.bbx";
private static String PROGRAM_NAME = "c:/basis/work/widget.bbj";
// private variables for executing bridge communications
private JavaBBjBridge m_bridge = null;
private BBjBridgeRequest m_request = null;
private BBjBridgeResponse m_response = null;
// private variables for storing request data and results
private boolean m_initialized = false;
private String m_widgetCount;
private String m_batteryCount;
private String m_userID;
private String m_bonusWidgets;
private String m_bonusMiles;
private String m_discount;
private String m_report;
private String m_consoleString = null;
/**
* It is assumed that the command line parameters to main() will be
* widgetCount -- number of widgets being ordered
* batteryCount -- number of batteries being ordered
* userID -- id of user
*
* It is also assumed that each of these params represents a numeric
* value and that userID >= 100
*
* Optionally a fourth parameter of the form -tTxx may be added to allow
* dot-stepping through the BBj code during execution
*
* So an expected call line would be
*
* java WidgetBridge 7 3 783
* OR
* java WidgetBridge 7 3 783 -tT0
*/
public static void
main(String p_argv[])
{
String resultString = null;
if(p_argv.length < 3)
{
System.out.println("\n\nUSAGE:\n" +
" java WidgetBridge widgetCount batteryCount userID\n" +
" OR\n" +
" java WidgetBridge widgetCount batteryCount userID" +
" -tTxx\n"); System.exit(-1);
}
else
{
try
{
WidgetBridge widgets;
if(p_argv.length > 3)
widgets = new WidgetBridge(p_argv[3]);
else
widgets = new WidgetBridge();
widgets.processOrder(p_argv[0],p_argv[1],p_argv[2]);
System.out.println(widgets.reportResults());
}
catch(BBjBridgeException e)
{
e.printStackTrace();
System.exit(-1);
}
}
System.exit(0);
}
WidgetBridge()
{
this(null);
}
WidgetBridge(String p_consoleString)
{
m_consoleString = p_consoleString;
}
/**
* processOrder
*/
public int
processOrder(String p_widgetCount, String p_batteryCount, String p_userID)
throws BBjBridgeException
{
m_widgetCount = p_widgetCount;
m_batteryCount = p_batteryCount;
m_userID = p_userID;
int ret = UNKNOWN_ERROR;
if(checkArgsNumeric())
{
// initialize and run the bridge
initBridge();
ret = runBridge();
m_report = createReport(ret);
}
else
{
m_report = "ERROR: widgetCount, batteryCount and userID " +
"must all be numeric";
}
return ret;
}
/**
* reportResults writes the results of running the BBj program
* to a PrintWriter.
*/
public String
reportResults()
{
return m_report;
}
/**
* runBridge() uses current data to run the bridge program and
* retrieves the results.
* @ret the release value of the BBj program that was executed
*/
private int
runBridge()
throws BBjBridgeException
{
// set the program name
m_request.setProgramName(PROGRAM_NAME);
// place input data into bridge request
m_request.put("widgetCount", m_widgetCount);
m_request.put("batteryCount", m_batteryCount);
m_request.put("userID", m_userID);
// run the bridge and obtain it's return value
int ret = m_bridge.runBBj(m_request, m_response, BRIDGE_TIMEOUT);
// retrieve the response data from bridge request
m_bonusWidgets = m_response.get("bonusWidgets");
m_bonusMiles = m_response.get("bonusMiles");
m_discount = m_response.get("discount");
return ret;
}
/**
* initBridge() will initialize bridge, request, response if not
* already initialized. initBridge() is no-op if already initialized
* @param p_consoleString can be be a string of the form "-tT0" to
* specify a terminal to be used for debugging the BBj code while
* it is running in this WidgetBridge. If this param is null or if it
* an empty string then the string should represent a valid Terminal ALIAS
* present in the config file.
*/
private void
initBridge() throws BBjBridgeException
{
if( ! m_initialized)
{
if(m_consoleString == null)
m_consoleString = "";
String cmdLine = " -c" + CONFIG_FILE + " " + m_consoleString;
m_bridge = JavaBBjBridgeFactory.createBridge(
cmdLine,true,true,false,true);
m_request = JavaBBjBridgeFactory.createBridgeRequest();
m_response = JavaBBjBridgeFactory.createBridgeResponse();
m_initialized = true;
}
}
/**
* checkArgsNumeric checks that input params are all numeric
*/
private boolean
checkArgsNumeric()
{
boolean ret;
try
{
// call parseInt to ascertain that all params are numeric
Integer.parseInt(m_widgetCount);
Integer.parseInt(m_batteryCount);
Integer.parseInt(m_userID);
ret = true;
}
catch(NumberFormatException e)
{
ret = false;
}
return ret;
}
private String
createReport(int p_returnValue)
{
String ret;
// write results to p_out
switch(p_returnValue)
{
case 0:
// print results to stdout
ret = "\n\nTest Results from running Widget test: " +
"\n bonus Widgets: " + m_bonusWidgets +
"\n bonus miles: " + m_bonusMiles +
"\n discount: " + m_discount;
break;
case READ_ERROR:
ret = "Bridge program experience error reading";
break;
case WRITE_ERROR:
ret = "Bridge program experience error reading";
break;
case UNKNOWN_ERROR:
default:
ret = "unknown error executing bridge program.";
break;
}
return ret;
}
public void
finalize()
{
m_bridge.close();
}
}
(end of listing)
...Article continued on next page
|