CORBA Program Development
Listing 2 shows the LJEventChannel.java
source, which defines two classes.
A D V E R T I S E M E N T
LogServiceImpl and LJEventChannel. The
LogServiceImpl class extends the _PushSupplierImplBase base class, and
implements LogServiceOperations. The LogServiceOperations class has the
capabilities necessary for the tie mechanism, which we will use to connect to
the LogServiceImpl object. The LogServiceImpl class provides the core
functionality for binding to the proper VisiBroker Event Service channel. Since
LogServiceImpl extends _PushSupplierImplBase, it is able to function in the role
of a Push Supplier vis-�-vis the Event Service (for more information, see last
month's article).
The meat of the LogServiceImpl class is in its constructor. When a new
LogServiceImpl object is created, the constructor first binds to the ORB via
an org.omg.CORBA.ORB.init call. Then, it
connects to a particular event channel, �channel_server�, in order to pass
strings via the Push Consumer proxy it creates. The actual process of connecting
to the Event Channel was covered in detail last month; however, we will
summarize the steps here briefly.
First, the bind method is called on the EventChannelHelper class that is part
of VisiBroker's Event Service. The Naming service is not necessary to connect to
the Event Service, because the osagent facilitates the binding by using its own
simple naming service.
Once an EventChannel is bound, a Push Consumer proxy is obtained from the
Event Channel, and then our LogServiceImpl object is connected to the proxy.
This allows us to make calls on the proxy. From this point on, any supplier who
calls the send method on our LogServiceImpl object will cause our
implementation to call the push method on its Push Consumer proxy. This is done
with the line _pushConsumer.push(message). Notice that, as usual, we've
packaged our string to be sent in an Any type, which the Event Service
requires for transmission.
Class LJEventChannel consists of a single main
method which, after binding to the ORB and initializing the Basic Object Adapter
for our object, creates a new LogServiceImpl object described above. The
tie method is used in the binding process, which means we will be using
delegation instead of inheritance in our communication with the object's
implementation. After we have tied to our new LogServiceImpl delegate named
�new_service�, we then bind that service object to the Naming Service under the
component path Linux Journal:LJEventChannel. This allows any client
object on any machine in the visible network to connect to our new_service
object via this naming convention, without having to know the name or IP address
of the hosting machine.
Once the new_service delegate has been bound to the Naming Service, the BOA
is advised that the object is ready and available and the implementation of the
server is complete.
At this point, an implementation of the LogService interface defined in the
IDL has been created and published and is now available for calls from clients
wishing to use its send method to post messages to the Event Channel.
Listing 3. PushSupplier.java
import org.omg.CosNaming.*;
import logging.*;
public class PushSupplier
{
public static void main(String args[])
{
// Initiliaze the ORB.
org.omg.CORBA.ORB orb =
org.omg.CORBA.ORB.init(args,null);
String logname;
if( args.length > 0 )
logname = args[0];
else
logname = "default supplier";
// bind to the Log Service
// LogService logger = LogServiceHelper.bind(orb,
// "LogService");
LogService logger = null;
try
{
org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService");
org.omg.CosNaming.NamingContext rootContext =
org.omg.CosNaming.NamingContextHelper.narrow(objRef);
NameComponent comp1 = new NameComponent(
"Linux Journal","");
NameComponent comp2 = new NameComponent(
"LJEventChannel","ec");
NameComponent [] name = {comp1, comp2};
org.omg.CORBA.Object rObjRef =
rootContext.resolve(name);
logger = LogServiceHelper.narrow(rObjRef);
}
catch(Exception e)
{
System.out.println("Exception: " + e);
}
if( logger == null )
{
System.out.println("logger is null");
return;
}
else
System.out.println("logger is NOT null");
System.out.println("bound the Log Service");
// use user-supplied args as the
// supplier's name, or use a default.
System.out.println("supplier
[" + logname + "] entering send loop");
while(true)
{
try
{
System.out.println("supplier
[" + logname + "] sending a string now...");
logger.send(
"this is the string from supplier [" + logname + "]");
Thread.sleep(2000);
}
catch( InterruptedException e )
{
System.out.println(
"Received InterruptedException: " + e);
}
}
}
}
Listing 3 shows PushSupplier.java, the supplier in our application. Class
PushSupplier consists entirely of a single main method, which after initializing
the orb with org.mg.CORBA.ORB.init, stores the name of the supplier which was
optionally entered on the command line when the supplier was started. This
arbitrary name allows you to name your suppliers Supplier1, Supplier2, etc., so
that you will know, on the consumer side, which supplier's string was obtained.
After initializing the ORB, a LogService reference named logger is
created. Then we enter a try block, which seeks to bind to the LogServiceImpl
object already created using the Naming Service. The supplier calls the
resolve_initial_references method on the orb object, obtains a root context,
creates the appropriate name components, and calls resolve
on the root context using the created name component array. The generic object
reference returned is then narrowed by a call to narrow using the
LogServiceHelper object.
Assuming the logger object is not null, we then enter a loop that
continually sends a string to the LogServiceImpl object via its send method.
This continues until the user interrupts the supplier with ctrl-C.
Listing 4.
import org.omg.CosEventComm.*;
import org.omg.CosEventChannelAdmin.*;
import com.visigenic.vbroker.services.CosEvent.*;
import java.io.DataInputStream;
public class PullConsumer extends _PullConsumerImplBase
{
public void disconnect_pull_consumer()
{
System.out.println(
"PullConsumer.disconnect_pull_consumer called");
}
public static void main(String[] args)
{
try
{
org.omg.CORBA.ORB orb =
org.omg.CORBA.ORB.init(args, null);
org.omg.CORBA.BOA boa = orb.BOA_init();
EventChannel channel = null;
PullConsumer ljPullConsumer = null;
ProxyPullSupplier pullSupplier = null;
DataInputStream in =
new DataInputStream(System.in);
System.out.println(
"about to call bind on channel_server");
channel = EventChannelHelper.bind(
orb,"channel_server");
System.out.println(
"Located event channel: " + channel);
ljPullConsumer = new PullConsumer();
boa.obj_is_ready(ljPullConsumer);
System.out.println("Created ljPullConsumer: "
+ ljPullConsumer);
pullSupplier =
channel.for_consumers().obtain_pull_supplier();
System.out.println("Obtained pull supplier: "
+ pullSupplier);
System.out.println("Connecting...");
System.out.flush();
pullSupplier.connect_pull_consumer(
ljPullConsumer);
System.out.println(
"Consumer entering Event Pull Loop...");
org.omg.CORBA.BooleanHolder hasEvent =
new org.omg.CORBA.BooleanHolder();
org.omg.CORBA.Any result = null;
while(true)
{
while(!hasEvent.value)
{
result = pullSupplier.try_pull(hasEvent);
}
System.out.println("Consumer pulled event: "
+ result.toString());
hasEvent.value = false;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Listing 4 shows the PullConsumer class, which extends the _PullConsumerImplBase
base class of the VisiBroker Event Service. After initializing the ORB and BOA,
the PullConsumer object attempts to bind to the Event Channel by calling the
bind method on the EventChannelHelper object. Then a new PullConsumer object is
created, which implements the disconnect_pull_consumer
method required by a PullConsumer. The reason a new PullConsumer must be created
is because we need an object reference to pass to the BOA's
obj_is_ready and the proxy Pull Supplier's
connect_pull_consumer method. Since we are in the main method which
is static, no �implicit this� reference is available to us. Therefore, we need
to create a new object in order to obtain
a reference to pass. Once a new PullConsumer object is created, the BOA is
advised that the object is ready. After this, a Pull Supplier proxy is created
via a call to the bound channel's obtain_pull_supplier
method. Once the proxy is created, the PullConsumer object is connected to the
proxy by calling connect_pull_consumer on the proxy, passing it the PullConsumer
object.
At this point, a while loop is entered, and the consumer continually
calls try_pull on the Pull Supplier proxy. If
the proxy finds an event, then that event is returned, the PullConsumer object
prints that message to standard output and the loop restarts.
You can try out this application by first running a single PushSupplier and
PullConsumer locally on the same Linux box. (See the README.install and
README.run instructions that accompany the code for details on the setup,
building and launching of the applications.) Then you might want to launch
another PushSupplier and notice that the PullConsumer automatically begins to
process events from the new PushSupplier as well. (You might want to name the
PushSuppliers as they are launched�see the README.run instructions on how to do
this.) Then launch a new PullConsumer over on the Windows (or other OS) box, and
watch how events from the two suppliers are conveyed to the two consumers, one
of which is now running on the Windows machine. Finally, launch another
PushSupplier on the Windows machine and watch how two consumers process strings
created and delivered to the same event channel by three separate suppliers.
Even though the code is simple, the project implemented here is quite capable
and has some broad implications which you should explore.
|