Novell is now a part of Micro Focus

Using the Novell Trader

Articles and Tips: article

SHELDON BAGLEY
Software Engineer Novell Trader
Java Technologies Group

01 Oct 1997


Introduction

The Novell Trader can be described as a "Yellow Pages" for locating distributed network objects. It is fully CORBA compliant and based on the OMG Trader specification. As explained in the last issue, the need for a trader compounds as the number of distributed software objects increase on a network. For example, locating all the auto repair shops in Los Angeles area that specialize in body work for foreign cars would be very difficult and time consuming with a white pages phone book. However, this same task is quite simple with the yellow pages.

Likewise, as the number of distributed objects on a network grow into the tens of thousands or even millions, locating the "best fit" for a piece of client software is next to impossible with a standard naming system. However, the trader categorizes objects into types and associates properties such as cost, speed, precision, stability, security, and so forth so a client object can easily locate the best server to handle its request. For a complete architectural overview of the Novell Trader Service, see the August issue of Developer Notes.

This article takes you step-by-step through the process of building a simple distributed object system from scratch and making it known on the network via the Novell Trader. We will build a very simplistic calculator so we can set our focus on learning how to use the trader and ORB. However, the same code we develop here will work for more complex distributed object systems.

To get started you need to install the Novell Trader and the VisiBroker for Java ORB on your machine. The Novell Trader can be downloaded from http://developer.novell.com/orb/documentation/trader. The ORB runtime can be downloaded from the same location or from the Visigenic web page. Follow the instructions to complete your Trader and ORB install. You can also download the source code for these examples from the trader web page. Create a new directory called "calcSample" and add it to your Classpath environment variable. You are now ready to begin.

We will first build a server object and register it with the trader. Next, we will build a client object that uses the trader to find services that satisfy its needs. We will finish by running our distributed system.

Building an Object Server and Registering It With the Trader

Our minimal calculator only has two methods add and subtract. However, the same trader and ORB connectivity we build here can be used for more complex systems. The interface definition language (IDL) for calculator is placed in Calc.idl and looks like:

interface Calc {

    long add (in long n1, in long n2);

    long sub (in long n1, in long n2);

}

Run the idl2java compiler to generate your framework of client stubs and server skeletons with the idl2java Calc.idl command. Your calcSample directory now contains:


Filename
Description

Calc.java

Java interface for Calc

_CalcImpBase.java

Skeleton server side code (implements Calc.java)

_example_Calc.java

Template for implementing your calc server

Calc.Helper

Client side helper methodsincluding the bind method

_st_Calc.java

Client side stub

The remaining generated files are for backward compatability and the ORB tie mechanism and may be discarded for this example. The file_example_Calc.javaextends_CalcImplBase (which implements Calc.java) and is our template for implementing the calc server which we will register/advertise with the trader. Copy _example_Calc.javato MyCalcServer.java to make your modifications so subsequent idl2java compiles don't overwrite your work. Here is the complete server source for MyCalcServer.java followed by an explanation:

// MyCalcServer.java - implementation of calc server

import java.util.*;

import org.omg.CosTradingRepos.ServiceTypeRepositoryPackage.*;

class myCalc extends _CalcImplBase {

  public myCalc(java.lang.String name) { super(name); }

  public myCalc() { super(); }

  public int add(int n1, int n2) { return n1+n2; }

  public int sub(int n1, int n2) { return n1-n2; }

}



public class MyCalcServer {

public static void main(String[] args) {

  try {

  // initialize the orb & boa (basic object adapter).

  org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();

  org.omg.CORBA.BOA boa = orb.BOA_init(); 

  

   // Instantiate a calculator named calc1  

  myCalc cObj = new myCalc("calc1");

  boa.obj_is_ready(cObj);

  

  // Add a calculator type if one doesn't exist

  org.omg.CosTradingRepos.ServiceTypeRepository typeObj;

  typeObj = org.omg.CosTradingRepos.ServiceTypeRepositoryHelper.bind(orb);

  try  {

  typeObj.add_type("calculator", "calc_if", new PropStruct[0], new String[0]);

  }

  catch (ServiceTypeExists e) {System.out.println("calculator already exists");}

  catch (org.omg.CORBA.UserException e) {System.out.println(e); }

  

  // bind to trader's register interface

  org.omg.CosTrading.Register regObj;

  regObj = org.omg.CosTrading.RegisterHelper.bind(orb);



  // build property sequence with just one property entry "cost = 50"

  

  org.omg.CosTrading.Property[] props = new org.omg.CosTrading.Property[1];

  org.omg.CORBA.Any costAny = orb.create_any();

  costAny.insert_long(50);

  props[0] = new org.omg.CosTrading.Property("cost", costAny);



  try  {

  String offerId = regObj.export(cObj, "calculator", props);

  } catch (org.omg.CORBA.UserException e) { System.out.println(e); }



  boa.impl_is_ready(); // Wait for incoming requests



  } catch(org.omg.CORBA.SystemException e) { System.err.println(e); }

}

}

Let's step through the code. The template for the first class (myCalc), is _example_Calc.java which was generated by the idl2java compiler. The first constructor in class myCalc is for persistently named objects and the second constructor is for transient objects. The return statements for methods add()and sub() provide the implementation. You will notice that myCalc is not public so myCalc and MyCalcServer can be in the same file.

The class MyCalcServer provides the trader and ORB connectivity. The code in this class can easily be reused for other distributed systems. We begin by initializing the orb. The orb object is then used to initialize the Basic Object Adapter (BOA) with the BOA_init call. We then instantiate myCalc and name it "calc1". Now we call boa.obj_is_ready to make "calc1" available to client objects on the network.

The next several lines bind us to the service type repository and add calculator as a type. These lines of code may not be of much interest since you will normally use the trader admin tool to add the calculator type. For simplicity, no super types or property templates were associated with the calculator type. These lines were added to eliminate the need of running the trader admin tool for this simple example.

Now we are ready to export our service with the trader. The trader itself is a distributed object system so we first bind to its register interface to obtain a register object "regObj." Next we prepare to make the export call by building the properties sequence (array) named props. Since we will only have one property associated with the "calc1" service offering, the props array only needs a length of one.

With more complex service offerings, there will likely be many properties. Properties are name value pairs, and in this example name ="cost", and value=50. The property value is a CORBA any type which can be a string, a long, a float, a char, or several other types. With this in mind, we define a CORBA.Anycalled costAny on the stack and insert a long of 50 as the value (this is done with the insert_long() method. The props only entry is cost with a value of 50.

We now make the export() call and pass in the calculator object reference (cObj), the service type/category ("calculator"), and the properties sequence (props). We now have our service registered with the trader! We finish by calling boa's impl_is_ready() method which enters a loop to wait for client requests. That completes the coding for MyCalcServer.

Now build your server object by typing:

>javac MyCalcServer.java

Note: In this example we only used the register export() method. The trader's register interface also includes methods to withdraw(), describe(), and modify() service offerings.

Building a Client that Finds Servers Via the Trader

With the server built and registered with the trader you are finished programming if you are in the business of providing software objects. However, if you also need to find distributed objects to consume, including the one you just built, then you are also interested in the trader's lookup interface.

The trader's lookup interface includes the query() method. This powerful method is the means by which a client object can obtain references to other objects that provide services meeting its requirements.

We now create a client class from scratch called CalcClient and place it in a file called CalcClient.java. This class is devoted mainly to making the query call. Here is the complete source followed by an explanation:

// CalcClient.java

public class CalcClient {

public static void main(String args[]) {

    try {

        org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();

        org.omg.CosTrading.Lookup lookup = org.omg.CosTrading.LookupHelper.bind(orb);

        

        // Setup query arguments

        org.omg.CosTrading.Policy[] policies = new org.omg.CosTrading.Policy[0];

        org.omg.CosTrading.LookupPackage.SpecifiedProps desired_props = 

                new org.omg.CosTrading.LookupPackage.SpecifiedProps();

        String[] props = new String[1];

        props[0] = "cost";

        desired_props.prop_names(props);



        // Setup query return parameters

        org.omg.CosTrading.OfferSeqHolder offer = 

                new org.omg.CosTrading.OfferSeqHolder();

        org.omg.CosTrading.OfferIteratorHolder offer_itr = 

                new org.omg.CosTrading.OfferIteratorHolder();

        org.omg.CosTrading.PolicyNameSeqHolder limits_applied = 

                new org.omg.CosTrading.PolicyNameSeqHolder();



        try  {

            lookup.query("calculator", "cost > 100", "min cost", policies, 

                    desired_props, 10, offer, offer_itr, limits_applied);

        } catch (org.omg.CORBA.UserException e) {}

        

        String objName = offer.value[0].reference._object_name();

        Calc calcObj = CalcHelper.bind(orb, objName); 

        System.out.println(objName+" thinks 3+3 = "+calcObj.add(3,3));

        

    } catch(org.omg.CORBA.SystemException e) { System.err.println(e); }

}

}

We begin by doing an orb init and passing the orb reference to the LookupHelper.bind() method to get a reference to the trader's lookup distributed object. As previously mentioned, the trader is made up of numerous distributed objects that we bind to with Helper classes that were originally generated by the ORB idl2java compiler.

We will now set up for the query() method call. We start by instantiating a zero length policies array so the trader's default policies will be used. The desired properties array "desired_props" indicates which properties we want returned with the offers that meet our needs. The props string array can be left empty if there is no interest in accessing returned property information. In this example we ask for cost information to be returned.

Next we create the return objects for the query. The first return parameter is the offer object (CosTrading.OfferSeqHolder) and is the only return argument we use in this example. We are now ready to make the query call. The first parameter indicates the desired type - "calculator." The constraint string "cost > 100" is the second argument which says - "only give me offers with cost less than 100." The third parameter is a preferences string "min cost" indicating that the ordering of the returned offers will be the least expensive calculators first.

After returning from the query, the offer.value array holds the offers. The object reference of the first offer is in the value array at position zero. We use the CORBA object reference to obtain the object name with the _object_name() call. Next we use the calc helper class to bind with the calculator server. We finish the CalcClient class by calling the add method and displaying the result. The CalcClient is finished!

Build the client object by typing:

>javac CalcClient.java

Running Your Distributed System

Now that you have programmed and built your distributed system, it is ready to run. However, before the objects are started the ORB and trader need to be running. First start the smart agent (osagent) to allow ORB connectivity. The osagent is located in your ORB bin directory.

>osagent

The Trader and TypeRepository also need to be running before you run your system. You can simply run the TypeRepository and Trader batch files from your trader directory, or go to the "com/novell/trader" directory and type:

>java TypeRepos
>java NovellTrader

Now start MyCalcServer and CalcClient:

>java MyCalcServer
>java CalcClient

Now you have one object server running and registered with the trader. You also have a single client that finds the server via the trader, binds to the server, and makes a method call. If you download these examples from our web page or the developers CD you will find several MyCalcServer versions that register different property values. You will also find additional CalcClients that use the Novell Trader for finding object servers that best meet their needs.

* Originally published in Novell AppNotes


Disclaimer

The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.

© Copyright Micro Focus or one of its affiliates