Chapter 6. Putting It All Together

Table of Contents

Collaborative Slider
The Subject
The Observer
The Server Application
The JavaBean
Running the Example
File Loader
The JavaBean
XML File

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.

Collaborative Slider

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.

The Subject

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 the Interface

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_    1
    #define _NETWORK_TEST_SLIDER_SUBJECT_IDL_    2
    
    #include <tweek/idl/Subject.idl>             3
  5 
    module networktest                           4
    {
       interface SliderSubject : tweek::Subject  5
       {
 10       void setValue(in long val);            6
          long getValue();                       7
       };
    };
    
 15 #endif                                       8
    
1 2 8

IDL files are run through the C preprocessor so that they may include external files. To prevent including the same source multiple times, the file must be enclosed in the traditional preprocessor protection block. This is exactly what is done in C/C++ header files.

3

All Tweek subject IDL files must include tweek/idl/Subject.idl. This is required so that the interface being defined can inherit from tweek::Subject.

4

Typically, using a module to contain the interface is recommended. Using a module defines a Java package and a C++ namespace (named networktest in this case).

5

The definition of the subject interface must inherit from tweek::Subject. If this is not done, there is no way that the subject interface will plug into the Subject Manager.

6 7

This simple interface has only two functions: setValue() and getValue(). The method getValue() takes a single read-only parameter of type long; getValue() returns a value of the same type.


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.)

Implementing the Interface in C++

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>                           1
    #include <SliderSubject.h>                                     2
 10 
    namespace networktest                                          3
    {
    
    /**
 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                    4
        , 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);                          5
 40 
       /**
        * Returns this subject's internal value.
        */
       virtual long getValue();                                    6
 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()                      7
       {
          return POA_networktest::SliderSubject::_this();
       }
    
 60 private:
       long mValue;   /**< Our value */                            8
    };
    
    } // End of networktest namespace
 65 
    
    #endif /* _SLIDER_SUBJECT_IMPL_H_ */
1

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 Observer.idl file.

2

This header is generated by the IDL compiler from SliderSubject.idl. In particular, it defines the class from which networktest::SliderSubjectImpl must inherit.

3

In SliderSubject.idl, shown in Example 6.1, “SliderSubject.idl”, the interface is in the networktest module. In the C++ implementation code, the module name corresponds to a namespace.

4

The interface implementation class must inherit from the IDL-generated class POA_networktest::SliderSubject and from tweek::SubjectImpl.

5 6

POA_networktest::SliderSubject defines two pure virtual methods that must be implemented. These correspond to the methods in SliderSubject.idl.

7

As of this writing, all subject implementations must contain an overriding version of the _this() method. This is due to the use of multiple inheritance. Note the return type and the return statement. These will vary for each subject implementation based on the name of the IDL-defined interface.

8

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>                   1
    
    namespace networktest
  5 {
    
    void SliderSubjectImpl::setValue(long value)
    {
       mValue = value;                               2
 10 
       // Notify any observers that our value has changed.  This is very
       // important.
       tweek::SubjectImpl::notify();                 3
    }
 15 
    long SliderSubjectImpl::getValue()
    {
       return mValue;                                4
    }
 20 
    } // End networktest namespace
1

Include the class declaration file, shown in Example 6.2, “SliderSubjectImpl.h”.

2

When invoked, the remote caller will pass a long value, and this saves the result into the servant's storage.

3

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 notify() method of the parent class.

4

Observers will invoke this method when requesting the current mValue.


The Observer

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.

Implementing the Observer in Java

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 ObserverPOA            1
 15 {
       public SliderObserverImpl(JSlider slider,                   2
                                 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()                                        3
       {
 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());            4
             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());                     5
       }
    
 50    private SliderSubject mSliderSubject = null;
       private JSlider       mSlider        = null;
    }
1

As an observer, this class must derive from tweek.ObserverPOA. This class is generated by the IDL compiler and is part of the Tweek Network Library (see the section called “Network Library”).

2

The constructor for this observer takes two arguments: a JSlider object reference and a networktest.SliderSubject object reference. The observer needs the latter argument so that it can query state information from the subject when notified of state changes. This is part of the subject/observer design pattern [Gam95].

3

As stated, all observers must implement update(). This will be invoked by the remote subject when its notify() method is invoked.

4

To get the updated state of the remote subject, the encapsulated subject reference's getValue() method is invoked. The value returned will be the most up-to-date information from the subject.

5

It may be convenient for the observer to implement a detach() method (though the name may vary). This is used when the Java application is shutting down to ensure that the remote subject does not have dangling references to observers that no longer exist. The only action required here is invoking the subject's detach() method to inform the subject that this observer is going away.


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.

The Server Application

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>                         1
    #include <vpr/Thread/Thread.h>
    #include <vpr/Util/Debug.h>
    
  5 #include <SliderSubjectImpl.h>                                2
    
    /**
     * This application starts the CORBA server for the C++ side
     * of the test.
 10  */
    int main(int argc, char* argv[])
    {
       tweek::CorbaManager mgr;                                   3
    
 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() )     4
 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();              5
    
 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();       6
    
 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");        7
                   }
                   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 )                                          8
 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;
    }
1 2

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”.

3 4

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

5

After the COBRA Manager has been initialized successfully, the Tweek Subject Manager must be created.

6

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 networktest::SliderSubject references will be made.

7

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.

8

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 'x'.


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.

The JavaBean

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.

Imports

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;                                       1

import java.awt.*;                                         2
import javax.swing.*;                                      3
import javax.swing.event.*;                                4
import org.omg.CORBA.BAD_PARAM;                            5
import org.vrjuggler.tweek.event.*;                        6
import org.vrjuggler.tweek.net.*;                          7
import org.vrjuggler.tweek.net.corba.*;                    8
import tweek.*;                                            9
1

This Bean is in a package corresponding to the IDL module defined in SliderSubject.idl. Using a consistent name throughout makes the code easier to manage and understand.

2 3 4

These imports bring in common Swing and AWT GUI classes. Different Beans will use different classes and packages.

5

In order to do proper narrowing of reference types, the exception org.omg.CORBA.BAD_PARAM must be caught.

6 7 8

These imports bring in the classes of the Tweek Event and Network libraries.

9

Tweek Java code generated by an IDL compiler will always be in the package tweek. This corresponds to the IDL module and to the C++ namespace.

Class Declaration

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 JPanel                                          1
   implements CommunicationListener                        2
{
...
}

class FrameListener extends TweekFrameAdapter              3
{
...
}
1

As a Tweek Panel Bean, there must be a class that derives from javax.swing.JComponent or some subclass thereof. In this case, the superclass is javax.swing.JPanel.

2

Since this Bean needs to access the remote Subject Manager, it needs to know when a CORBA service is available. By implementing org.vrjuggler.tweek.net.CommunicationListener, the Bean will be informed by the Tweek Java GUI whenever the communication state with a CORBA service changes.

3

This Bean also listens for TweekFrameEvents so that it can shut itself down cleanly. To be informed of such events, a helper class that extends org.vrjuggler.tweek.event.TweekFrameAdapter exists.

Member Variables

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();     1

private JPanel mSliderPanel = new JPanel();                2
private JSlider mDataSlider = new JSlider();               3

private SliderObserverImpl mSliderObserver = null;         4
1

This is the containing layout for the entire Bean.

2 3

The JSlider used for visually representing the subject's numeric data is mDataSlider, and it will be contained within mSliderPanel.

4

The networktest::SliderSubject observer will be stored within mSliderObserver.

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.

Handling CORBA Communications

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)              1
    {
       // 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();            2
       SubjectManager mgr = corba_service.getSubjectManager();      3
    
 15    Subject subject = mgr.getSubject("SliderSubject");           4
       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);     5
 25    }
       catch (BAD_PARAM narrow_ex)                                  6
       {
          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);   7
 45       corba_service.registerObject(mSliderObserver,
                                       "SliderObserver");           8
    
          // 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());           9
    
          // 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());          10
          mDataSlider.addChangeListener(
             new SliderChangeListener(slider_subject)
          );                                                        11
       }
 60 }
    
    /**
     * Implements the Tweek CommunicationListener interface needed
     * for being informed when existing ORB connections are closed.
 65  */
    public void connectionClosed(ConnectEvent e)                    12
    {
       if ( mSliderObserver != null )
       {
 70       mSliderObserver.detach();                                 13
          mSliderObserver = null;
       }
    }
1

If the event delivered to the Bean is a newly opened connection, there is a new CORBA service available, so we will create a new observer to communicate through that service.

2 3

The first step that must be taken is retrieving the org.vrjuggler.tweek.net.corba.CorbaService object from the event. This is needed as the basis for all further actions. With the CORBA service reference, we can request the Subject Manager reference. The result is a CORBA reference to the remote Subject Manager, a servant within the C++ application.

4

Through the Subject Manager, we request subjects using symbolic names. In this case, we request the subject with the hard-coded name “SliderSbuject”.

5 6

The call to getSubject() returns a reference of type tweek.Subject, but we need to narrow it to networktest.SliderSubject so we can use it. Among the code generated by the IDL compiler is a class named networktest.SliderSubjectHelper. Its narrow() method is used to narrow the CORBA reference type to a more specific type. If the narrowing fails, an exception of type org.omg.CORBA.BAD_PARAM is thrown, and it is best to handle it here.

7

Once we have the subject reference, we create an observer servant that we will attach to the subject. This is where networktest.SliderObserverImpl comes into the picture.

8

The observer servant must be registered with the local CORBA service so that references to it can be created. This single statement takes care of all the POA activation behind the scenes.

9

Now that the servant is registered with the CORBA service, a reference to it can be passed to the remote subject in order to attach the observer to the subject.

10

Once the initial references are passed around, we need to update the GUI slider to the current value held by the subject.

11

As part of the collaborative slider, we need to register a change listener with the GUI slider so that it will be informed of changes made by the local user. These changes are reported to the remote subject so that other users get the update. The class SliderChangeListener is shown later.

12 13

If the event delivered to the Bean was the closing of an existing connection, the CORBA service is being shut down. When the CORBA service is shut down, we need to detach the local observer from the remote subject to prevent further update notification attempts for this observer.

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.

Handling Frame Events

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)               1
   {
      return bean.frameClosing();                               2
   }

   private NetworkTest bean = null;
}
1

This class overrides only the method invoked when the Tweek GUI frame is closing. If this method returns false, then the GUI frame will not close.

2

The handling of the frame closing event is delegated to NetworkTest.frameClosing(), which in turn calls NetworkTest.disconnect() to shut down the CORBA connection cleanly.

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);
}

Handling Local Slider Change Events

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() )                     1
      {
         mSliderSubject.setValue(source.getValue());            2
      }
   }

   private SliderSubject mSliderSubject = null;
}
1 2

Within the stateChanged() method, we check to see if the user is done adjusting the slider. If so, the remote subject is updated so that its internal value matches that of the local slider. Other users are automatically updated when this happens.

XML File

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"?>                          1
<beanlist
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        2
    xsi:noNamespaceSchemaLocation="http://www.vrjuggler.org/tweek/xsd/1.1/beanlist.xsd">
  <guipanel name="Network Tester">                              3
    <file                                                       4
       name="${TWEEK_BASE_DIR}/share/tweek/beans/NetworkTestBean.jar"
       class="networktest.NetworkTest" />
    <tree path="/Beans" />                                      5
  </guipanel>
</beanlist>
1

Well-formed XML requires this line.

2

Tweek XML Bean files are beanlist documents. There must be exactly one beanlist per file, but the list itself can contain multiple Beans. In this element, we also provide the information needed to validate this document using the XML Schema beanlist.xsd. In this case, we use the schema that does not employ XML namespaces. Use of the schema for validation is optional, but it is recommended.

3

The Bean we have been developing is a GUI Panel Bean, and this indicates that fact.

4

To find the Bean, a path must be specified. In this example, we will put the XML and JAR files in $TWEEK_BASE_DIR/share/tweek/beans. Note the use of curly braces around the environment variable. This is required. The class attribute names the class that will be instantiated, and in this case, that is networktest.NetworkTest. This name corresponds more to the fully qualified name of the class stored within the JAR file.

5

Panel Beans are loaded into a tree data structure and visualized using a viewer. This specifies the path within the tree using a UNIX-style path. The path is arbitrary and will be constructed as needed when the Bean is loaded.


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.

Running the Example

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:

  1. Run a CORBA Naming Service. This is required so that the C++ and Java ORBs can resolve symbolic references.

  2. 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.

  3. Run the C++ application.

  4. Run the Tweek Java GUI. This will find and load all the Beans in $TWEEK_BASE_DIR/share/tweek/beans.

  5. 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.