Table of Contents
Based on the information presented in the previous chapters, we can combine everything into an examples. In this chapter, we present the step-by-step process for using the Tweek Java and C++ APIs.
In this example, we explain how to develop a simple Tweek
interface. The goal is to have a “collaborative” slider
in a Java GUI component. The value displayed by the slider is retained
by a C++ application so that multiple independent sliders can show the
same value. The steps explained here are highly representative of the
normal steps to be followed when using the Tweek Java and C++ APIs.
The structure of the following sections lays out the order of the
steps taken. An example makefile that goes along with the code
presented can be found in the section called “SliderSubject” of Appendix A, Compiling Example Code. The full source for the examples
presented in this section can be found in
$TWEEK_BASE_DIR/share/test/NetworkTestBean.
To begin, the subject interface must be defined in IDL and implemented in C++. The interface itself will be “compiled” into Java and C++ code. Both ends of the communication channels must know the interface in order for the references to be used, thus requiring the generation of code for both languages.
Creating an IDL interface involves writing an IDL file. For
this example, we will be storing an integer variable in a C++
servant. The Java GUIs will need to read and write the value, so
we need two methods: getValue() and
setValue(). The type being passed between
ORBs will be
long, a 32-bit integer. Depending on the target
language, this will map to the corresponding type of the same
size.
Example 6.1. SliderSubject.idl
1 #ifndef _NETWORK_TEST_SLIDER_SUBJECT_IDL_#define _NETWORK_TEST_SLIDER_SUBJECT_IDL_
#include <tweek/idl/Subject.idl>
5 module networktest
{ interface SliderSubject : tweek::Subject
{ 10 void setValue(in long val);
long getValue();
}; }; 15 #endif
![]()
The file SliderSubject.idl must be
“compiled” by an IDL compiler. For use
with Tweek, the interface must be compiled into Java and C++ code.
The generated Java code will be used solely for communicating with
CORBA networktest.SliderSubject
references. The generated C++ code will be extended to provide an
implementation of the
networktest::SliderSubject
interface. (The implementation will be a CORBA servant object to which
references will be made by remote Java code.)
After running an IDL compiler to generate the stub CORBA
code, the interface must be implemented. In particular, there will
be pure virtual methods in SliderSubject.h
that must be implemented. The implementing class will be the CORBA
servant holding the
actual data visualized in the Java GUI slider.
Example 6.2. SliderSubjectImpl.h
1 #ifndef _SLIDER_SUBJECT_IMPL_H_ #define _SLIDER_SUBJECT_IMPL_H_ #include <tweek/tweekConfig.h> 5 #include <vector> #include <tweek/CORBA/SubjectImpl.h>#include <SliderSubject.h>
10 namespace networktest
{ /** 15 * This class is an extension to the base Tweek SubjectImpl class. * It uses multiple inheritance with that class and with the * generated CORBA class corresponding to the IDL for * SliderSubject. */ 20 class SliderSubjectImpl : public POA_networktest::SliderSubject
, public tweek::SubjectImpl { public: 25 SliderSubjectImpl() : tweek::SubjectImpl(), mValue(0) { /* Do nothing. */ ; } 30 virtual ~SliderSubjectImpl() { /* Do nothing. */ ; } 35 /** * Sets this subject's internal value. */ virtual void setValue(long value);
40 /** * Returns this subject's internal value. */ virtual long getValue();
45 /** * This overriding method is needed so that the correct type * is returned when the _this() method is invoked. Without * this method, an object of type tweek::Subject_ptr would 50 * be returned. * * XXX: It may be possible to remove this requirement in * the future. */ 55 networktest::SliderSubject_ptr _this()
{ return POA_networktest::SliderSubject::_this(); } 60 private: long mValue; /**< Our value */
}; } // End of networktest namespace 65 #endif /* _SLIDER_SUBJECT_IMPL_H_ */
| These files will always be included by implementations
of Tweek subject derived classes. The first contains the
declaration for the basic Tweek subject implementation. The
second contains the C++ code generated from the Tweek
|
| This header is generated by the IDL compiler from
|
| In |
| The interface implementation class must inherit from
the IDL-generated class
|
|
|
| As of this writing, all subject implementations must
contain an overriding version of the
|
| This is the actual value being stored by the C++ servant. |
In SliderSubjectImpl.h, the most
important parts to note are the use of multiple inheritance, the
declarations of the SliderSubject
interface methods, and the implementation of
_this(). The implementations of
setValue() and
getValue() are shown next in Example 6.3, “SliderSubjectImpl.cpp”.
Example 6.3. SliderSubjectImpl.cpp
1 #include <vpr/Util/Debug.h> #include <SliderSubjectImpl.h>namespace networktest 5 { void SliderSubjectImpl::setValue(long value) { mValue = value;
10 // Notify any observers that our value has changed. This is very // important. tweek::SubjectImpl::notify();
} 15 long SliderSubjectImpl::getValue() { return mValue;
} 20 } // End networktest namespace
| Include the class declaration file, shown in Example 6.2, “SliderSubjectImpl.h”. |
| When invoked, the remote caller will pass a long value, and this saves the result into the servant's storage. |
| Because the subject's state has been modified, all
attached observers must be notified. This is a very
important step that must be taken in this method. Note that
it invokes the |
| Observers will invoke this method when requesting the
current |
The observer does not define its own specialized IDL
interface. Instead, it makes use of the existing Tweek basic
observer (tweek.ObserverPOA in Java). The method
update() must be implemented. The remainder
of the observer implementation is centered around communication with
a SliderSubject object reference. All
observer code is written in Java. The only C++ code for observers is
part of the Tweek library, and it is generated entirely by the
IDL
compiler.
For every subject interface defined in IDL, a corresponding observer class must be written in Java. Without an observer, there is no way for the Java and C++ sides to conduct useful two-way communication. At best, the Java GUI could request a subject reference and manipulate the C++ application through the reference, but the communication would be entirely one-way.
In Example 6.4, “SliderObserverImpl.java”, we
show the complete Java implementation of an observer corresponding
to the tweek::SliderSubject
interface defined earlier. (The JavaBean that uses this observer
is explained in the section called “The JavaBean”.)
The main focus of this observer is to update its contained
JSlider whenever the state of the
corresponding subject changes.
Example 6.4. SliderObserverImpl.java
1 package networktest; import javax.swing.DefaultBoundedRangeModel; import javax.swing.JSlider; 5 import tweek.*; /** * Implementation of the Observer side of the Tweek * Subject/Observer pattern. It must extend tweek.ObserverPOA 10 * so that instances of this class can be registered as CORBA * servants. In addition, CORBA references to the servants must * be capable of being attached to remote subjects. */ public class SliderObserverImpl extends ObserverPOA15 { public SliderObserverImpl(JSlider slider,
SliderSubject subject) { mSlider = slider; 20 mSliderSubject = subject; } /** * Implements the required method in tweek.ObserverPOA. The 25 * remote subject will invoke this method whenever it is * notified of a change. */ public void update()
{ 30 // If we have a valid slider object, we need to update // its value to whatever our subject has. if ( mSlider != null ) { DefaultBoundedRangeModel model = 35 (DefaultBoundedRangeModel) mSlider.getModel(); model.setValue(mSliderSubject.getValue());
mSlider.repaint(); } } 40 /** * Detaches this observer from our subject. This is needed * when shutting down a CORBA connection. */ 45 public void detach() { mSliderSubject.detach(this._this());
} 50 private SliderSubject mSliderSubject = null; private JSlider mSlider = null; }
| As an observer, this class must derive from
|
| The constructor for this observer takes two arguments:
a |
| As stated, all observers must implement
|
| To get the updated state of the remote subject, the
encapsulated subject reference's
|
| It may be convenient for the observer to implement a
|
This example demonstrates that observers do not have to be complex to be usable. While this example is purposely simple, it should illustrate that developers of observers do not necessarily have to make their implementations complicated. As will be shown in the section called “The JavaBean”, the JavaBean that uses this observer completes the picture and provides users with a GUI slider that can be manipulated by any number of simultaneous users.
Now that we have the subject and observer ready to go, we can
make an application that uses them. The following example is a
(relatively) simple C++ application that starts the CORBA
Manager, creates the Subject Manager, registers a
networktest::SliderSubject servant,
and then waits for the user to press 'x' to exit.
The use of exceptions may appear unfamiliar to some C++ programmers.
CORBA makes use of exceptions as a cross-language mechanism to
report errors, and thus, there must be proper exception handling
code for the application to work correctly.
Example 6.5. SliderSubjectApp.cpp
1 #include <tweek/CORBA/CorbaManager.h>#include <vpr/Thread/Thread.h> #include <vpr/Util/Debug.h> 5 #include <SliderSubjectImpl.h>
/** * This application starts the CORBA server for the C++ side * of the test. 10 */ int main(int argc, char* argv[]) { tweek::CorbaManager mgr;
15 // The first thing we have to do is initialize the Tweek // CORBA Manager. If this fails, we're out of luck. try { if ( mgr.init("corba_test", argc, argv).success() )
20 { vpr::ReturnStatus status; // Once the CORBA Manager is initialized, we need to // create a Subject Manager. This will hold our 25 // SliderSubject object. try { status = mgr.createSubjectManager();
30 // If we were able to create the Subject Manager, // now we register our objects with it. if ( status.success() ) { // First, create real instances of the C++ 35 // object that will be the CORBA servant. This // must be allocated on the heap. networktest::SliderSubjectImpl* slider_subj = new networktest::SliderSubjectImpl();
40 // Now we try to register the subject and give // it a symbolic, easy-to-remember name. try { mgr.getSubjectManager()-> 45 registerSubject(slider_subj, "SliderSubject");
} catch (...) { 50 vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Failed to register subject\n" << vprDEBUG_FLUSH; } 55 // We are done with our pointer to the servant. slider_subj->_remove_ref(); } } catch (CORBA::Exception& ex) 60 { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Caught an unknown CORBA exception when " << "trying to register!\n" << vprDEBUG_FLUSH; 65 } if ( ! status.success() ) { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 70 << "Failed to register Subject Manager instance\n" << vprDEBUG_FLUSH; } std::cout << "Press 'x' to exit" << std::endl; 75 char input; // Loop forever so that we can act sort of like // a server. while ( 1 )
80 { std::cin >> input; if ( input == 'x' ) { break; 85 } else { vpr::System::msleep(100); } 90 } } else { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 95 << "CORBA failed to initialize\n" << vprDEBUG_FLUSH; } } catch (...) { 100 vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Caught an unknown exception!\n" << vprDEBUG_FLUSH; } vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 105 << "Exiting\n" << vprDEBUG_FLUSH; return 0; }
| These two headers are typically needed. The first includes the declaration of the Tweek CORBA Manager, and the second is the subject implementation declaration, shown in Example 6.2, “SliderSubjectImpl.h”. |
| In order to use CORBA through Tweek, the CORBA Manager must be created and initialized. Any number of these may be created, but in general, only one is needed per application |
| After the COBRA Manager has been initialized successfully, the Tweek Subject Manager must be created. |
| Once we have a valid Subject Manager, we must register
subjects with it in order for object references to be passed
out by CORBA. This creates the servant to which
|
| Once the servant is created, it is registered with the Subject Manager. The Subject Manager will activate the servant within the POA so that references to it can be created and returned to clients. |
| After the subject is registered, all the work is done.
This application now just waits for clients to request
references. It will exit when the user enters
' |
The application shown in Example 6.5, “SliderSubjectApp.cpp” is purposely simple. There are many ways to use the CORBA Manager and the Subject Manager. For example, an object registry could be built on top of the Subject Manager so that only specific types of servant objects may be registered. Servant registration could be automated in object constructors. The C++ API is intended to be simple to enhance its usability and flexibility, and the application shown in the example is just that: an example.
We have finally reached the point at which we implement a JavaBean that can visualize the numeric data held by the C++ slider subject. Such a JavaBean must be defined as a Tweek Panel Bean. (Refer back to Chapter 2, JavaBeans if these statements seem unfamiliar or confusing.)
The JavaBean shown in the following example is typical of one
that uses the Tweek Network library to take advantage of CORBA
facilities. The code for the Bean is longer than previous examples,
and because of this, it will be split into multiple code blocks.
Each will be discussed in turn. The full code is in one file:
NetworkTest.java.
Please note that the details of setting up the GUI elements used by the Bean are left out. The code in this case was generated by JBuilder and could easily vary from Bean to Bean. For the full code, please refer to the aforementioned locations.
The slider JavaBean uses common Java Swing classes, a CORBA exception class, the Tweek Event library, the Tweek Network library, and the Java code generated by an IDL compiler. The following explains how each of these are imported into the main Bean class.
package networktest;import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import org.omg.CORBA.BAD_PARAM;
import org.vrjuggler.tweek.event.*;
import org.vrjuggler.tweek.net.*;
import org.vrjuggler.tweek.net.corba.*;
import tweek.*;
![]()
Now we can begin writing the Bean code. Besides the class
SliderObserverImpl, this Bean has only one
public class: NetworkTest. It will provide
the GUI representation of the numeric data. The declaration of the
class and its package-visible helper follow.
/** * This is an example of a JavaBean that Tweek can load dynamically. It holds * a JSlider that acts as an Observer in the Tweek CORBA Subject/Observer * pattern implementation. * * @version 1.0 */ public class NetworkTest extends JPanelimplements CommunicationListener
{ ... } class FrameListener extends TweekFrameAdapter
{ ... }
Before delving into the methods of the
networktest.NetworkTest class, it will be
helpful to review the member variables used throughout the class.
Refer back to this section if there is any confusion regarding the
use or the type of some member variable in the method
implementations.
private BorderLayout mBeanLayout = new BorderLayout();private JPanel mSliderPanel = new JPanel();
private JSlider mDataSlider = new JSlider();
private SliderObserverImpl mSliderObserver = null;
![]()
Note that the observer is stored in a member variable initialized to null. It will be assigned a value when a CORBA service becomes available and the corresponding subject can be requested. The object itself is stored as a member variable so that it can be accessed by all the methods of the class.
The most complex part of this Bean is the handling of CORBA
communication events delivered by the Tweek GUI. All of the
handling in this example will be done in the methods
connectionOpened() and
connectionClosed(). The steps that must
be followed are straightforward, but there are errors that must be
handled properly. It is the error handling that can make the code
look daunting, not the use of the Tweek Network library.
1 /** * Implements the Tweek CommunicationListener interface needed * for being informed of new connections with remote ORBs. */ 5 public void connectionOpened(CommunicationEvent e){ // The first thing to do is get the CORBA service object from // the event. We need this so we know to whom we are are // connecting. Once we have the CORBA service, we get its 10 // Subject Manager since that's what contains the actual // subjects we need. CorbaService corba_service = e.getCorbaService();
SubjectManager mgr = corba_service.getSubjectManager();
15 Subject subject = mgr.getSubject("SliderSubject");
SliderSubject slider_subject = null; // Try to narrow the Subject object to a SliderSubject object. // If this fails, it throws a CORBA BAD_PARAM exception. In 20 // that case, we open a dialog box saying that the narrowing // failed. try { slider_subject = SliderSubjectHelper.narrow(subject);
25 } catch (BAD_PARAM narrow_ex)
{ JOptionPane.showMessageDialog( null, 30 "Failed to narrow subject to SliderSubject", "SliderSubject Narrow Error", JOptionPane.ERROR_MESSAGE ); } 35 // Ensure that slider_subject is a valid object just to be // safe. if ( slider_subject != null ) { 40 // First, we need a Java object that implements the // Observer. That object must be registered with the Java // CORBA service. mSliderObserver = new SliderObserverImpl(mDataSlider, slider_subject);
45 corba_service.registerObject(mSliderObserver, "SliderObserver");
// Now that the observer is registered, we can attach it // to the subject. The subject needs to know who its 50 // observers are so that it can notify them of updates. slider_subject.attach(mSliderObserver._this());
// Now we set the slider in our GUI to be whatever value // the remote subject is holding for us. 55 mDataSlider.setValue(slider_subject.getValue());
mDataSlider.addChangeListener( new SliderChangeListener(slider_subject) );
} 60 } /** * Implements the Tweek CommunicationListener interface needed * for being informed when existing ORB connections are closed. 65 */ public void connectionClosed(ConnectEvent e)
{ if ( mSliderObserver != null ) { 70 mSliderObserver.detach();
mSliderObserver = null; } }
It is important to note that the use of hard-coded subject names is not recommended. In this example, the subject name is hard-coded for simplicity. The Tweek Subject Manager allows accessing code to request a list of all registered subjects. Using this information, it is possible to present the Java GUI user with a list of subjects from which they can make a selection.
This Bean makes an effort to shut down open CORBA
connections when the Tweek GUI is closed. It does this through a
helper class that extends
org.vrjuggler.tweek.event.TweekFrameAdapter,
overriding only one method:
frameClosing(). This method calls back
into the NetworkTest class, invoking its own
frameClosing() method. For more
information about the other Tweek GUI frame events delivered
through the interface
org.vrjuggler.tweek.event.TweekFrameListener,
refer to the Tweek Java API reference.
public class NetworkTest
extends JPanel
implements CommunicationListener
{
...
public boolean frameClosing()
{
disconnect();
return true;
}
...
}
class FrameListener extends TweekFrameAdapter
{
public FrameListener(NetworkTest bean)
{
this.bean = bean;
}
public boolean frameClosing(TweekFrameEvent e)
{
return bean.frameClosing();
}
private NetworkTest bean = null;
}For the helper class FrameListener to
be notified of Tweek GUI frame events, it must be registered with
the Tweek event listener registry (a singleton). This step is
performed in the NetworkTest constructor,
as shown below.
public NetworkTest()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
mFrameListener = new FrameListener(this);
EventListenerRegistry.instance().
registerListener(mFrameListener,
TweekFrameListener.class);
}A helper class is used to handle local change events in the slider. The class is a private inner class within the Bean and is defined as follows:
private class SliderChangeListener implements ChangeListener
{
public SliderChangeListener(SliderSubject subject)
{
mSliderSubject = subject;
}
public void stateChanged(javax.swing.event.ChangeEvent e)
{
JSlider source = (JSlider) e.getSource();
if ( ! source.getValueIsAdjusting() )
{
mSliderSubject.setValue(source.getValue());
}
}
private SliderSubject mSliderSubject = null;
}With the Bean implementation done, the XML file that describes the Bean must be written. All Beans loaded by the Tweek Java GUI are described by an XML file. In Example 6.6, “NetworkTestBean.xml”, we show the XML file for the Bean we have been developing.
Example 6.6. NetworkTestBean.xml
<?xml version="1.0" encoding="UTF-8"?><beanlist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.vrjuggler.org/tweek/xsd/1.1/beanlist.xsd"> <guipanel name="Network Tester">
<file
name="${TWEEK_BASE_DIR}/share/tweek/beans/NetworkTestBean.jar" class="networktest.NetworkTest" /> <tree path="/Beans" />
</guipanel> </beanlist>
After the Bean is compiled into a JAR file, the JAR file and
XML file need to be copied into
$TWEEK_BASE_DIR/share/tweek/beans. This is
the default path that Tweek searches for Beans at startup, and it
is a convenient place to put this example Bean.
With all the coding done and the code compiled, we can run the C++ application and connect multiple instances of the Tweek Java GUI to it. The steps to run the C++ application are as follows:
Run a CORBA Naming Service. This is required so that the C++ and Java ORBs can resolve symbolic references.
Since Tweek uses omniORB for a C++ ORB (see Appendix B, CORBA Implementations for more information on this),
the environment variable $OMNIORB_CONFIG must be
set. This gives the full path to an omniORB configuration file
that omniORB will load at runtime.
Run the C++ application.
Run the Tweek Java GUI. This will find and load all the
Beans in
$TWEEK_BASE_DIR/share/tweek/beans.
Within the Java GUI, connect to the CORBA Naming Service started in step 1. The way the Bean is written, it will request the subject held by the C++ application automatically, and the slider will be updated to the current value.