Tweek

The Programmer's Guide

Patrick Hartling

1.0

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with the Invariant Sections being Appendix D, GNU Free Documentation License, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in Appendix D, GNU Free Documentation License.

$Date: 2006-11-15 10:24:10 -0600 (Wed, 15 Nov 2006) $


Table of Contents

Preface
I. Introduction
1. Tweek
C++ API Design Overview
Subject
Observer
CORBA Manager
Subject Manager
CORBA
Interface Definition Language
Supported Languages
2. JavaBeans
Bean Categories
Service Beans
Viewer Beans
Panel Beans
Generic Beans
XML
II. Programming
3. IDL
4. C++
Deriving from tweek::SubjectImpl
Using the CORBA Manager
Using the Subject Manager
Subject Manager Initialization
tweek::SubjectManagerImpl API
5. Java
GUI Library
Bean Library
Event Library
Network Library
Bean Delivery Library
6. Putting It All Together
Collaborative Slider
The Subject
The Observer
The Server Application
The JavaBean
Running the Example
File Loader
The JavaBean
XML File
7. C++ as a Client
The CORBA Service
Example Client Application
StringSubject Interface
StringSubject Interface Implementation
Observer Implementation
Client Application
8. Python
III. Appendices
A. Compiling Example Code
SliderSubject
File Loader
B. CORBA Implementations
C. Legal
D. GNU Free Documentation License
PREAMBLE
APPLICABILITY AND DEFINITIONS
VERBATIM COPYING
COPYING IN QUANTITY
MODIFICATIONS
COMBINING DOCUMENTS
COLLECTIONS OF DOCUMENTS
AGGREGATION WITH INDEPENDENT WORKS
TRANSLATION
TERMINATION
FUTURE REVISIONS OF THIS LICENSE
ADDENDUM: How to use this License for your documents
Bibliography
Glossary
Index

List of Examples

2.1. PlexusGraphView.xml snippet
2.2. Viewers.xml
3.1. Subject.idl
3.2. CustomSubject.idl
4.1. CustomSubjectImpl.h
4.2. CustomSubjectImpl.cpp
4.3. TweekApp.cpp
4.4. TweekApp.cpp
6.1. SliderSubject.idl
6.2. SliderSubjectImpl.h
6.3. SliderSubjectImpl.cpp
6.4. SliderObserverImpl.java
6.5. SliderSubjectApp.cpp
6.6. NetworkTestBean.xml
6.7. FileOpenTestBean.xml
7.1. StringSubject.idl
7.2. StringSubjectImpl.h
7.3. StringSubjectImpl.cpp
7.4. StringObserverImpl.h
7.5. StringObserverImpl.cpp
7.6. client.cpp: Required Header Files
7.7. client.cpp: Implementation of main(), Part I
7.8. client.cpp: Implementation of main(), Part II
7.9. client.cpp: Implementation of chooseSubjectManager()

Preface

This book is the programmer's guide for Tweek. The main focus is how to use the features and capabilities of Tweek to enable cross-platform Java graphical user interfaces (GUIs) to communicate with C++ applications.

The history of Tweek dates back to April 2000, though the basis for its existence comes from circa 1997. At Iowa State University's Virtual Reality Applications Center, the idea of using a Java GUI to communicate with, and possibly perform manipulations on, C++ applications is the foundation for dynamic reconfiguration of VR Juggler. To that end, the Java application VjControl was developed specifically for that purpose. VjControl was started in 1997 and has been under development ever since.

Building on the basic idea of a Java GUI that could communicate with a C++ application, a viewer was written for the Distributed Shared Object (DSO) software system written in April 2000. This iteration of DSO was for a class project (Computer Science 552 taught by Dr. Johnny Wong). At the time, we (Allen Bierbaum and I) felt that CORBA could be used as a way to share arbitrary object-based information between applications on a network. The Java GUI was used to visualize the network of CORBA-connected nodes and to manipulate the network by disconnecting and reconnecting the nodes. In the end, CORBA was not the right solution to this problem, but the basis for network visualization was founded. Using CORBA for communication between the C++ code and Java code was relatively easy, however.

In April 2001, we were again faced with a class project. Based on the results of the CORBA-based DSO, we felt that it would be best to implement a peer-to-peer software multicasting system that would be more efficient than CORBA. This new system, called Plexus, would still offer the same capabilities of cross-platform data distribution, but we had to give up the inherent cross-language support offered by CORBA. Again, we wanted a Java GUI for visualizing the network, and we started with the DSO viewer code. Instead of using CORBA, which we had had some difficulties with the previous year, we chose to use Java's built-in Remote Method Invocation (RMI) system. While RMI is very easy to use between two Java applications, Java to C++ communication is difficult. It requires the use of the Java Native Interface (JNI) so that natively compiled C++ code can communicate in memory with a loaded Java virtual machine (JVM). All of the Java code and RMI was collected into a package called PlxView (“plex-view”).

Despite the difficulty of writing JNI code, the RMI solution was effective, for the most part. In September 2001, yet another project loomed. By this time, we were fed up with RMI, and we decided to go back to CORBA solely for communicating between Java and C++. Indeed, by using CORBA, we could write network visualization software in any language and communicate through the same channels.

Beyond just communicating with the C++ applications, we had high hopes for using RMI to download Java code at run time to add custom visualization panels to the Java GUI dynamically. The Java code would come in the form of JavaBeans. While PlxView was designed to be modular from the start, we had not implemented the code for downloading the JavaBeans. To begin that work, I started writing code on a flight from Dallas/Ft. Worth to London, England, the evening of October 1, 2001. While at a CAVE workshop in Stockholm, Sweden, I took advantage of down time and late nights to extend PlxView to load and use JavaBeans.

When I returned to the United States after the week in Stockholm, PlxView had evolved into what could be called version 0.0.0 of the Tweek Java GUI (sans CORBA, however). Its primary purpose was still Plexus network visualization. Design discussions with other members of the Juggler Team evolved the code into what is now the Tweek Java API. The Plexus-specific parts were separated into what is now the PlxView Bean, and the remaining code was moved into the new Tweek source tree.

The only remaining piece was CORBA support. Another student, Andrew Schwantes, had been experimenting with CORBA in a smaller system, and his C++ CORBA code was used as a starting point for the Tweek C++ API. After much discussion with Allen Bierbaum, the code was re-written entirely to make use of the Observer design pattern, and the Subject Manager was added. After all of that, the Java CORBA code was relatively trivial to add. And thus, the foundation for Tweek was in place by November 2001. By this time, it was already in use by the Fall 2001 Plexus class project.

Part I. Introduction

Chapter 1. Tweek

Programmers using Tweek must first understand what it is and what at does, at least at a conceptual level. Tweek has two parts: a Java API and a C++ API. The two provide very distinct functionality, but they share a common bond through the use of remote method calls on shared objects. Objects defined in the C++ programming language can be accessed by objects defined in the Java programming language. Similarly, objects defined in Java can be accessed by C++ code. The C++ objects may be visualized and manipulated using the Tweek Java graphical user interface (GUI), a tool written using the Tweek Java API. This cross-language functionality is achieved through the use of remote method calls.

Some readers may be familiar with remote procedure calls (RPC), a remote programming system first introduced by Sun Microsystems that uses the procedural programming paradigm. Remote method calls differ primarily through the use of the object-oriented programming paradigm. Tweek is implemented in two object-oriented languages and thus lends itself very well to a system implementing remote method calls.

In the remainder of this chapter, we present a high-level description of the Tweek C++ API. The Java API design is much more complex and is not included in this chapter. (Refer to Chapter 5, Java for details on using the Java API.) Most users of Tweek need to know more about the C++ side of Tweek than the Java side. This is because Tweek is designed around the philosophy of a simple Java GUI interacting with a potentially complex C++ application.

C++ API Design Overview

The heart of the Tweek software system implements the Observer pattern [Gam95]. This design pattern is used to define the relationship between the Java GUI (observer) and the C++ application (subject). Within this section, we explain how the subject and observer are used. Moving beyond the subject/observer pattern, we also explain the Subject Manager and the CORBA Manager. These four components make up the entirety of the C++ design.

Subject

The subjects in Tweek are part of the C++ applications. The communication “channels” are defined by the subjects' interfaces. An observer is attached to a subject, and whenever the state of a subject changes, it notifies all of its attached observers.

The Tweek C++ API defines the basic subject interface (tweek::Subject) that implements the subject pattern [Gam95]. Users of the Tweek C++ API derive from the base subject implementation (tweek::SubjectImpl) and extend it by adding their own interface methods. This extension is twofold. First, an interface must be defined using the Interface Definition Language (IDL). Then, the interface must be implemented in C++ code. (Refer to the section called “Interface Definition Language” for more information about IDL in Tweek.)

Observer

The observers in Tweek are (traditionally) part of the Java GUI[1]. They observe the state of the remote subjects and can provide a visual rendering of that state.

Programmers do not define interfaces for the observers. Instead, the Tweek C++ API defines a basic observer interface called tweek::Observer. There is no “standard” observer implementation that corresponds to tweek::SubjectImpl. By design, observers must correspond directly with subjects, but there is no need to extend the basic observer interface using IDL. Observer implementations simply inherit from the basic observer class (tweek.ObserverPOA in Java or POA_tweek::Observer in C++) and implement the update() method. Other extensions can be added in the custom observer class, of course, but an implementation of update() is always required.

CORBA Manager

CORBA tends to have a high learning curve. It is a very powerful system, but that power leads to a lot of complexity. To reduce the complexity of starting and using an ORB, Tweek provides a CORBA Manager. Its primary function is to initialize a local ORB. It does this by creating the Portable Object Adapter (POA), resolving the initial reference to the Naming Service, and starting a thread for the ORB to handle requests.

Note

An explanation of the POA is beyond the scope of this book. Users of Tweek do not have to use the POA directly because the CORBA Manager and Subject Manager hide these details. Interested readers are referred to [Hen99] for more information about the POA and CORBA in general.

Once the local ORB is initialized, the Subject Manager (discussed next) must be created. This is also done through the CORBA Manager because the Subject Manager is a CORBA object. The newly created Subject Manager will be a servant object to which CORBA references can be created.

Refer to the section called “CORBA” for more information about CORBA and its use in Tweek. For the most part, the use of CORBA is an implementation detail. Users of the Tweek C++ API must initialize the CORBA Manager, however, and it is important to understand its place in the overall system.

Subject Manager

The Tweek Subject Manager exists to simplify the use of CORBA further. At a very high level, it acts as a simplified, specialized CORBA Naming Service. Users of Tweek register subject servants with the Subject Manager. The Subject Manager handles the CORBA registration and activation of the servants. After being registered, subjects are accessed using symbolic strings. The strings are user-defined and do not necessarily conform to any CORBA-related standard. They are, in essence, identifiers used to look up the subject within the Subject Manager's collection of known subjects.

CORBA

CORBA, the Common Object Request Broker Architecture, is a powerful tool for distributed programming. It is a language-independent standard specified by the Object Management Group (OMG). Many CORBA implementations, both free and commercial, exist for a wide variety of languages (e.g., C, C++, Java, Perl, Python, and Smalltalk). CORBA allows communication between software written in any programming language running on any operating system on any hardware architecture. It handles all serialization and de-serialization of objects and method parameters so that programmers do not have to worry about endian issues and other system incompatibilities.

Before going further with the discussion of CORBA in Tweek, readers must be familiar with some terminology. In CORBA, the physical object to which references are made is called a servant. The servant is an instance of some class that implements an interface and derives from CORBA::Object (or org.omg.CORBA.Object in Java). The actual details of servant implementations are postponed for later sections. For now, it is important to remember that there will be an object located in physical memory on some machine, and references will be made to that object through CORBA. Users acquire references by looking up the object by name in what is known as the CORBA Naming Service. The Naming Service has objects registered within its database, and clients request references from the database. When the reference is made available, methods may be invoked on it. Since the physical object resides in another memory space, this will create network traffic, though it is entirely transparent to the programmer.

CORBA uses Object Request Brokers (ORBs) to manage locally registered objects and to communicate with remote objects. The remote objects are managed by ORBs that reside locally on the machines that have the servants. Two ORBs communicate with each other using a standard protocol. In this case, that protocol is the Internet Inter-ORB Protocol (IIOP). IIOP is a new addition to Version 2.3 of the CORBA standard. It allows two ORBs written by different vendors to communicate and inter-operate. This capability is crucial to the correct functionality of Tweek and many other CORBA-based software systems.

Within the scope of Tweek, CORBA is used to enable transparent communication between C++ applications and the Tweek Java GUI. C++ objects registered with a local ORB are made available to the Java GUI through the Subject Manager. Beyond this, CORBA exists mostly "behind the scenes" so that developers of Tweek-based software do not have to learn very much about CORBA. Programmers must understand the Interface Definition Language, however, and this is explained next.

Interface Definition Language

The Interface Definition Language (IDL) is used by the CORBA standard to define the interfaces for remotely accessible objects. An IDL file looks very much like a simple C++ class declaration in a header file, though data members are not allowed in the interface. Thus, IDL is used exclusively to define the methods of the objects and external data structures that may be passed as arguments to those methods.

The interfaces alone are not sufficient to implement objects that may be handled by CORBA. A language-specific implementation must be written so that servants can be instantiated and registered with an ORB. To implement an interface, an IDL compiler must first be used to generate skeleton code for a specific language from the IDL file. Using the generated code, an implementation is then written. In Chapter 3, IDL, we explain in more detail how to use IDL to define interfaces.

Supported Languages

As discussed above, a very powerful feature of CORBA is its language independence. As of this writing, Tweek itself includes support for C++ and Java as the primary langauges. Support for generating the stub code needed to access Tweek Subjects through Python was added in early August 2003, and a PyQt-based GUI is being written so that developers can use Python and Qt for making GUI panels instead of Java. There is no restriction, other than time and resources, that prevents the addition of support for other languages. In this section, we explain how C++, Java, and Python are used in Tweek.

C++

A key part of the overall Tweek design is that complex, high-performance applications will be written in C++. While this may not necessarily be the case in every situation, this is the assumption made for the design and implementation of the Tweek C++ API. As mentioned previously, support could be added for other languages so that they too may fulfill the role of C++ in Tweek.

C++ Subjects

Using the C++ API on the server side to create subjects, programmers activate a local ORB using the CORBA Manager. Once an ORB is available, servants that will act as subjects can be registered with the Subject Manager. The subjects are activated within the local POA by the Subject Manager, thus alleviating some work for programmers. Once activated, the subjects may be accessed remotely through CORBA by code written in any language—including C++.

C++ Observers

Using the C++ API on the client side to create observers, programmers again activate a local ORB using the CORBA Service. Once an ORB is available, servants that will act as observers can be registered with the local POA via the CORBA Service. Once activated, the observers can be attached to remote subjects that may be written in any langauge. C++ observer code is very similar to Java observer code.

Java

In the Java programming language, the Swing API provides developers with a very nice suite of classes for writing cross-platform GUIs. When developing Tweek, we took advantage of Swing and JavaBeans technology [Jbe02] to write a generalized GUI framework. Users can plug components (Beans) into this framework at runtime to extend its functionality. The Beans can get access to remote C++ objects through the CORBA services provided by the Tweek Java API. Similar to the C++ API, the use of CORBA in Java has been simplified so that programmers can use it with little effort and without a comprehensive understanding of CORBA in general.

Programmers will use Java as part of their Tweek programming to write JavaBeans. Compared to the potential complexity of the GUI code for Beans, little CORBA programming must be done in Java. Beans may be as simple or as complex as necessary to meet the needs of individual projects. More information about JavaBeans is provided in Chapter 2, JavaBeans.

Python

With Python, we use PyQt [Pyq03] as the GUI interface. PyQt is highly portable, and a GPL version makes it easy to develop non-commercial, high-performance user interfaces. Because PyQt wraps Qt which in turn utilizes the native windowing system, user interfaces developed with PyQt tend to perform much better than Swing-based Java GUIs. Of course, natively compiled C++ that uses Qt directly would perform better still, but Python provides a degree of portability not offered (directly) by C++. We have used PyQt successfully to develop GUIs that run on desktops as well as on PDAs that include Qtopia.



[1] Beginning with Tweek 0.13, helper classes for writing C++ observers are included with the C++ API. Users of older versions can make use of observers written in C++ (or any other programming language with a CORBA implementation), but the application developers will have to write the CORBA client code entirely from scratch.

Chapter 2. JavaBeans

The Tweek Java GUI uses JavaBeans to be more flexible and accessible to programmers. The GUI is a framework into which graphical and non-graphical components may be “plugged”. Graphical components add interaction functionality. Non-graphical components extend internal functionality, oftentimes needed by the graphical components. Conceptually, this follows the traditional use of plug-in architectures wherein the components are discovered dynamically and added into the larger framework. In the case of the Tweek Java GUI, the plug-ins will fit into one of four categories, the most important of which is Panel Bean.

Bean Categories

There are four types of Beans that may be loaded by the Tweek Java GUI. They are categorized based on functionality and what is known about them in advance. The following lists the four categories in order of decreasing a priori knowledge.

  1. Service Beans

  2. Viewer Beans

  3. Panel Beans

  4. Generic Beans

Service Beans

Services encapsulate functionality that may be useful to other parts of the Tweek system or dynamically loaded code. The entire interface for Service Beans must be known when the code using the service is compiled. This is because the using code needs to be able to take advantage of the service. Because Service Beans may be loaded dynamically, using code must be prepared for the case when the Bean containing the service was not found. In other words, code that uses services cannot necessarily assume that the service will be available.

Not all services are loaded dynamically as Beans. Some services are loaded statically because they are needed by core components. These include the Environment Service and the Global Preferences Service. There is a guarantee that the code for these services will always be available. This guarantee is especially important because the Tweek core needs to add information to the Environment Service at startup. The Global Preferences Service is needed to configure the overall behavior of the Tweek GUI.

Viewer Beans

Viewer Beans provide a rendering of the tree of Panel Beans (discussed next). They provide the viewer component of the model/view pattern [Gam95]. All Viewer Beans are loaded dynamically, and the active viewer can be changed at runtime by editing the global preferences. This feature is realized through the flexibility of the model/view pattern.

Viewer Beans must implement the org.vrjuggler.tweek.beans.BeanModelViewer interface. To simplify implementation, they may be derived from org.vrjuggler.tweek.beans.DefaultBeanModelViewer, a class that implements aspects of the interface that are unlikely to vary between viewer implementations. The use of the interface is needed so that the GUI frame can assume certain behaviors about the viewer.

Panel Beans

Most programmers using Tweek will write Panel Beans. These provide custom interfaces for whatever users need. In most cases, a Panel Bean will provide a graphical interface that can manipulate and/or control a C++ application, but developers are not strictly limited to this use.

Only one assumption is made about Panel Beans: the primary class for the Bean must be a subclass of javax.swing.JComponent. Optionally, the primary class may implement one or more publicly provided interfaces that provide the Java GUI with more information about the capabilities of the Bean. When loaded, the GUI checks to see what, if any, interfaces are implemented by the Bean. Based on the results, special actions may be taken to provide the Bean with extended functionality.

For example, Beans that can load files should implement org.vrjuggler.tweek.beans.FileLoader. When the Bean is focused in the viewer, the File menu will be modified to enable the Open, Save, and Close items. If the user selects one of these items, the Bean is informed and can take appropriate customized actions. The result in this case is context-specific loading and unloading of files.

Generic Beans

Nothing at all is assumed about Generic Beans. This Bean category is provided so that other Beans can do their own dynamic code loading. For example, a Bean that uses a factory pattern may want to have the “workers” loaded dynamically based on some criteria. Thus, the functionality of the factory can be changed dynamically.

The Tweek Java GUI does not use Generic Beans itself. These are provided more for users of the Tweek Java API. It is up to those programmers to decide how to handle the Generic Beans on a case-by-case basis.

XML

All JavaBeans loaded by the Tweek Java GUI are describe by at least one XML file. The XML file can contain information about many Beans or about a single Bean. The XML file itself is a “beanlist” document. The four Bean categories, described above, each have an XML element that has children giving information about the specific Bean. The elements are <service>, <viewer>, <guipanel>, and <generic>.

All Bean XML entries must contain a <file> element. Through its source attribute, this element provides the path to the JAR file that contains the full Bean code. When specifying the JAR file path, environment variables may be used. They must use the syntax ${ENV_VAR} (the curly braces are required). The class element gives the fully qualified name of the class stored within the JAR file that will be instantiated. The extension .class must not be specified. This is to allow the use of serialized classes which have the extension .ser. The Tweek Bean-loading code will figure out what is available and take the right actions.

In addition to the <file> element, a <dependencies> element may be specified. Dependencies of the Bean may be named as external JAR files or other Beans may be listed therein. The <dependencies> element may contain zero of more elements of type <jar> and/or <bean>. The <jar> element has a single attribute, path, which gives the path (a semi-colon separated list of directories) where the JAR file may be found. The contents of the <jar> element defines the name of the JAR file. The <bean> element's contents defines the name of the Bean that the current Bean depends on.

Of the four Bean categories, the XML for Panel Beans can contain the most information. In addition to the previously mentioned elements, Panel Bean entries may have two optional elements: <tree> and <icon>. The element <tree> specifies the path within the Bean tree hierarchy where the Panel Bean will be placed. The path is given as a /-separated list of directories. If the named path does not exist when the Bean is loaded, it will be created. The element <icon> names a custom icon for the Bean and a tool-tip. An example of a Panel Bean XML entry is shown in Example 2.1, “PlexusGraphView.xml snippet”. Note that this is not the full file—it is only the <guipanel> element for a single Bean.

Example 2.1. PlexusGraphView.xml snippet

<guipanel name="Graph View">
  <file name="${PLX_BASE_DIR}/share/plexus/beans/PlexusGraphView.jar" 
        class="plx.graphview.GraphView" />
  <tree path="/" />
  <dependencies>
    <jar path="${PLX_BASE_DIR}/share/plexus/beans">openjgraph.jar</jar>
    <jar path="${PLX_BASE_DIR}/share/plexus/beans">jgraph.jar</jar>
    <jar path="${PLX_BASE_DIR}/share/plexus/beans">PlexusComm.jar</jar>
  </dependencies>
  <icon source="jar:file:${PLX_BASE_DIR}/share/plexus/beans/PlexusGraphView.jar!/plx/graphview/icon.gif"
        tooltip="Plexus Network Graph Visualization" />
</guipanel>

This shows the use of all the elements that may be children of <guipanel>. Note that the source attribute of <icon> gets its icon image using a JAR URL.

Another example XML file is shown in Example 2.2, “Viewers.xml”. This is the actual file used to load the two Viewer Beans that come with the Tweek distribution. This is a complete file containing two Viewer Bean entries.

Example 2.2. Viewers.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">
  <viewer name="Tree Viewer">
    <file name="${TWEEK_BASE_DIR}/share/tweek/beans/Viewers.jar" 
          class="org.vrjuggler.tweek.treeviewer.BeanTreeViewer" />
  </viewer>
  <viewer name="Icon Viewer">
    <file name="${TWEEK_BASE_DIR}/share/tweek/beans/Viewers.jar" 
          class="org.vrjuggler.tweek.iconviewer.BeanIconViewer" />
  </viewer>
</beanlist>

Part II. Programming

In the following chapters, we present the basic information needed to start programming with Tweek. There will be discussion covering IDL, C++, Java, and CORBA. To use Tweek effectively, a good understanding of C++ and Java is required. The IDL aspect of Tweek is minimal, and programmers familiar with basic object-oriented concepts should be able to understand IDL code easily. Discussion related to CORBA is based on the brief introduction given in the section called “CORBA”. The Tweek Java and C++ APIs are designed to hide most CORBA details. Whenever possible, references are given to good sources of information on all of the aforementioned topics.

Before proceeding, it is important to know that Tweek is designed and implemented to work with VR Juggler 1.1 and 2.0. It can be used with VR Juggler 1.0, but there have been reports of conflicts occurring between VR Juggler 1.0 and the VR Juggler Portable Runtime (VPR) that is part of VR Juggler 1.1. The Tweek C++ code uses VPR for threading, but it is possible to replace the VPR objects with VR Juggler 1.0 thread objects.

Chapter 3. IDL

In this chapter, we present the basic information needed to define interfaces that will be used by Tweek. This is not a detailed introduction to IDL programming. Readers are referred to [Hen99].

IDL “programming” means defining interfaces. In the scope of Tweek and CORBA, the interfaces declare what operations may be performed on CORBA references. The arguments and return values may be of several basic types including, but not limited to, string, int, long, and float. These types are specified in a language-independent manner. When the IDL compiler generates the code for a specific language, the language-specific types that correspond to the IDL types are used.

In Example 3.1, “Subject.idl”, we show the Tweek Subject interface. Note the similarity to a C++ header file. The IDL file can be included by other IDL files, and thus it must “protect” the contents in the same manner as a header file. The actual Subject interface is defined within the tweek module. An IDL module corresponds to a C++ namespace or to a Java package. The interface itself has three methods: attach(), detach(), and notify(). The first two take a read-only argument of type Observer. The fact that the argument is read-only (to the server) is specified by the in modifier. Other modifiers are out (sent from server to client) and inout (initialized by the client, writable by the server). The third method, notify(), takes no arguments, and none of the methods have a return type.

Example 3.1. Subject.idl

#ifndef _TWEEK_SUBJECT_IDL_
#define _TWEEK_SUBJECT_IDL_

#include <tweek/idl/Observer.idl>

module tweek
{

interface Subject
{
   void attach(in Observer o);
   void detach(in Observer o);
   void notify();
};

};

#endif

By definition, all objects are passed by reference in CORBA. The modifier stating the readability and/or writability in the IDL file determines how the referenced object may be modified, if at all, within the method.

Applications that make use of Tweek will define custom interfaces that extend the Subject interface. For example, consider a custom subject that maintains a floating-point value. It could have the following interface:

Example 3.2. CustomSubject.idl

#ifndef _CUSTOM_SUBJECT_IDL_
#define _CUSTOM_SUBJECT_IDL_

#include <tweek/idl/Subject.idl>

module mymod
{

interface CustomSubject : tweek::Subject
{
   float getValue();
   void setValue(in float v);
};

};

#endif

In this interface, we define two methods: getValue() and setValue(). The implementation of this interface would of course include these methods and would derive from the implementation of the Subject interface.

Chapter 4. C++

Writing C++ code that makes use of Tweek is not difficult, though it often requires some good planning. With the current code base, the C++ side of things maintains the state information through an implementation of the Tweek Subject interface. Instances of such an implementation may need to communicate with other parts of a given application, and it is important to define these relationships well. In other words, as a maintainer of application state information, the subject implementation should have easy access to that state information.

Furthermore, developers must keep in mind that there may be asynchronous execution of application code as a result of using Tweek. The local ORB runs in its own thread, and as such, it executes methods of servants from that thread. Whatever the servant does, it should be thread-safe with respect to the rest of the application.

In this chapter, we cover each aspect of writing C++ code that uses the Tweek API. We begin by explaining how to make a custom subject implementation. Then, we discuss the use of the CORBA Manager from user-level code. We conclude the chapter with an overview of using the Subject Manager.

Deriving from tweek::SubjectImpl

To create a custom subject implementation, you must derive from two classes: the abstract class that defines the custom interface and tweek::SubjectImpl. Referring back to the interface shown in Example 3.2, “CustomSubject.idl”, the basic C++ class declaration would appear as follows:

Example 4.1. CustomSubjectImpl.h

  1 #ifndef _CUSTOM_SUBJECT_IMPL_H_
    #define _CUSTOM_SUBJECT_IMPL_H_
    
    #include <tweek/CORBA/SubjectImpl.h>
  5 #include <CustomSubject.h>
    
    namespace mymod
    {
    
 10 class CustomSubjectImpl : public POA_mymod::CustomSubject,      1
                              public tweek::SubjectImpl             2
    public:
       CustomSubjectImpl() : mValue(0.0f)
       {
 15       ;
       }
    
       virtual ~CustomSubjectImpl()
       {
 20       ;
       }
    
       virtual float getValue();                                    3
    
 25    virtual void setValue(float v);                              4
    
       mymod::CustomSubject_ptr _this()                             5
       {
          return POA_mymod::CustomSubject::_this();
 30    }
    
    private:
       float mValue;
    };
 35 
    }
    
    #endif
1 2

Here we declare our parent classes, POA_mymod::CustomSubject and tweek::SubjectImpl. The first is code generated by the IDL compiler, and the second is included as part of the Tweek C++ API. Both are necessary for this custom interface to work correctly as a Tweek subject.

3 4

These two declarations correspond to the CustomSubject interface defined in Example 3.2, “CustomSubject.idl”. The implementations of these methods are not shown here, but they are required for the code to compile. That is, the declarations in POA_mymod::CustomSubject are pure virtual methods, and an instance of mymod::CustomSubjectImpl cannot be created unless these methods are implemented.

5

Overriding the method named _this() is required due to the diamond inheritance tree created by deriving from POA_mymod::CustomSubject and tweek::SubjectImpl. Both of these classes derive from tweek::Subject. The _this() method plays a critical role in the CORBA communication, and it is imperative that it return the correct type to the caller when invoked on a servant instance. Without this override, the returned type will be tweek::Subject_ptr, and attempts to narrow to mymod::CustomSubject_ptr will fail.

Note the namespaces used on this method. The return type is mymod::CustomSubject_ptr, which corresponds to the namespace in which the classes CustomSubjectImpl and CustomSubject are defined. To get the actual value to return, POA_mymod::CustomSubject (one of the two parent classes) is used.


The implementations of getValue() and setValue() are fairly obvious, though they are presented here for the sake of completeness. Note, however, that setValue() changes the state of the subject, and thus any observers must be notified of the change. The implementations are shown in the following example.

Example 4.2. CustomSubjectImpl.cpp

#include <CustomSubjectImpl.h>

namespace mymod
{

float CustomSubjectImpl::getValue()
{
   return mValue;
}

void CustomSubjectImpl::setValue(float v)
{
   mValue = v;
   tweek::SubjectImpl::notify();
}

}

The key point to note is the call to tweek::SubjectImpl::notify() in the setValue() implementation. In general, anything that modifies the state of the subject requires that this method be invoked. Note also that the method is fully qualified so that we are sure to call the correct implementation.

Using the CORBA Manager

Initializing the CORBA Manager is straightforward, but it does require exception handling. If the exceptions are not handled correctly, applications will abort if an exception is thrown but not caught. Refer to a C++ reference for more information about exceptions and exception handling in C++.

The following example shows a main() function for an application that performs all the Tweek initialization steps. We separate the discussion into two parts: one part for the CORBA Manager and one part for the Subject Manager (discussed in the next section).

Example 4.3. TweekApp.cpp

  1 #include <vpr/vpr.h>
    #include <vpr/Thread/Thread.h>
    #include <vpr/Util/Debug.h>
    #include <tweek/CORBA/CorbaManager.h>                         1
  5 
    #include <CustomSubjectImpl.h>                                2
    
    /**
     * This application starts the CORBA server for the C++ side
 10  * of the test.
     */
    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                                                        4
       {
 20       if ( mgr.init("example", argc, argv).success() )        5
          {
             vpr::ReturnStatus status;
    
             // Once the CORBA Manager is initialized, we need
 25          // to create a Subject Manager.  This will hold our
             // CustomSubject 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() )
                {
 35                // First, create real instances of the C++
                   // object that will be the CORBA servant.  This
                   // must be allocated on the heap.
                   mymod::CustomubjectImpl* custom_subj =
                      new mymod::CustomSubjectImpl();
 40 
                   // Now we try to register the subject and give
                   // it a symbolic, easy-to-remember name.
                   try
                   {
 45                   mgr.getSubjectManager()->
                         registerSubject(slider_subj, "CustomSubject");
                   }
                   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)
                   << "Failed to register Subject Manager instance\n"
 70                << vprDEBUG_FLUSH;
             }
    
             std::cout << "Press 'x' to exit" << std::endl;
             char input;
 75 
             // 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)
                << "CORBA failed to initialize\n"
 95             << vprDEBUG_FLUSH;
          }
       }
       catch (...)                                               6
       {
100       vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
             << "Caught an unknown exception!\n"
             << vprDEBUG_FLUSH;
       }
    
105    vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
          << "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 4.1, “CustomSubjectImpl.h”.

3

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. Here, we declare an instance of tweek::CorbaManager on the stack.

5

Next, we must initialize the CORBA Manager using the method tweek::CorbaManager::init(). The first argument provides a unique (ideally) identifier for the local Portable Object Adapter (POA). The second and third arguments are argc and argv respectively. They come in through the argument list of main() and represent the command-line arguments. Any arguments relevant to ORB initialization are removed from argv, and argc is decremented accordingly (it is passed by reference).

4 6

To ensure that no exceptions go uncaught, we enclose the bulk of main() in a try/catch block that catches any exception. This is handled by the argument list passed to the catch block, (...). This is the equivalent of catching java.lang.Exception in Java.


Using the Subject Manager

To demonstrate use of the Subject Manager, we begin by revisiting the main() function examined in the previous section. This time, we will focus our attention on the code related to the Subject Manager only. We will also explain how to use the extended API of the Subject Manager implementation.

It is important to know that the Subject Manager is a CORBA object that can be accessed by remote code. In the following example, the methods used are defined in the class tweek::SubjectManagerImpl, the C++ implementation of the tweek::SubjectManager interface. The details of how the Subject Manager is handled through CORBA are largely irrelevant for most users of Tweek. Simply bear in mind that the Subject Manager is accessibly remotely and that it simplifies the use of CORBA in general.

Subject Manager Initialization

In order to use the Tweek Subject Manager, it must be initialized. Each CORBA Manager should have a single Subject Manager associated with it. If not, use of Tweek will be much more difficult because the CORBA Manager and the Subject Manager together hide most of the details relating to the use of CORBA. The following example shows how to initialize the Subject Manager using the CORBA Manager object created earlier.

Example 4.4. TweekApp.cpp

  1 #include <vpr/vpr.h>
    #include <vpr/Thread/Thread.h>
    #include <vpr/Util/Debug.h>
    #include <tweek/CORBA/CorbaManager.h>
  5 
    #include <CustomSubjectImpl.h>
    
    /**
     * This application starts the CORBA server for the C++ side of
 10  * the test.
     */
    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
       {
 20       if ( mgr.init("corba_test", argc, argv).success() )
          {
             vpr::ReturnStatus status;
    
             // Once the CORBA Manager is initialized, we need to
 25          // create a Subject Manager.  This will hold our
             // CustomSubject object.
             try
             {
                status = mgr.createSubjectManager();              1
 30 
                // If we were able to create the Subject Manager,
                // now we register our objects with it.
                if ( status.success() )
                {
 35                // First, create real instances of the C++
                   // object that will be the CORBA servant.  This
                   // must be allocated on the heap.
                   mymod::CustomubjectImpl* custom_subj =
                      new mymod::CustomSubjectImpl();             2
 40 
                   // Now we try to register the subject and give
                   // it a symbolic, easy-to-remember name.
                   try
                   {
 45                   mgr.getSubjectManager()->
                         registerSubject(custom_subj,
                                         "CustomSubject");        3
                   }
                   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.
                   custom_subj->_remove_ref();
                }
             }
 60          catch (CORBA::Exception& ex)
             {
                vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
                   << "Caught an unknown CORBA exception when "
                   << "trying to register!\n"
 65                << vprDEBUG_FLUSH;
             }
    
             if ( ! status.success() )
             {
 70             vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
                   << "Failed to register Subject Manager instance\n"
                   << vprDEBUG_FLUSH;
             }
    
 75          std::cout << "Press 'x' to exit" << std::endl;
             char input;
    
             // Loop forever so that we can act sort of like
             // a server.
 80          while ( 1 )                                          4
             {
                std::cin >> input;
                if ( input == 'x' )
                {
 85                break;
                }
                else
                {
                   vpr::System::msleep(100);
 90             }
             }
          }
          else
          {
 95          vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
                << "CORBA failed to initialize\n" << vprDEBUG_FLUSH;
          }
       }
       catch (...)
100    {
          vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
             << "Caught an unknown exception!\n" << vprDEBUG_FLUSH;
       }
    
105    vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
          << "Exiting\n" << vprDEBUG_FLUSH;
    
       return 0;
    }
1

After the COBRA Manager has been initialized successfully, the Tweek Subject Manager must be created. We do this by invoking the method tweek::CorbaManager::createSubjectManager() on our tweek::CorbaManager instance.

2

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 mymod::CustomSubject references will be made.

3

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. We register it with the symbolic name “CustomSubject” that can be referenced later by remote objects.

4

After the subject is registered, all the work is done. This simple application now just waits for clients to request references. It will exit when the user enters 'x'.


Note that all the code relating to the Subject Manager is enclosed within another try/catch block. This block only catches exceptions of type CORBA::Exception. Anything more general is caught by the larger try/catch block.

tweek::SubjectManagerImpl API

The class tweek::SubjectManagerImpl has some methods that are not part of the IDL-specified interface. One such method is registerSubject(), which was used in the preceding example. Some other methods that may be of interest to users are described in the following subsections.

setApplicationName()

public void setApplicationName(const std::string& appName);

This method can be used to set one of the application-specific identifiers within the Subject Manager. Namely, it sets the application name identifier. These identifiers are used to aid users in choosing a Subject Manager instance when making remote connections to applications.

setUserName()

public void setUserName(const std::string& userName);

Similar to setApplicationName(), this method allows users to tell the Subject Manager their user name. When multiple users are all running the same application (and are thus using the same parameter to setApplicationName()), this provides another level of uniqueness. If this method is not used, the Subject Manager will try to get the user name through the $USER environment variable.

addInfoItem()

public void addInfoItem(const std::string& key,
                        const std::string& value);

If the previous two built-in Subject Manager identifiers are not enough, the method addInfoItem() allows users to define their own unique identifiers. The first parameter is the identifier, and the second is its (ideally) unique value. Users are free to add any key/value pairs they need in order to aid in the selection of a Subject Manager at runtime.

Chapter 5. Java

In this chapter, we present the libraries that make up the Tweek Java API. In general, we only provide a general overview of the libraries. Interested readers are referred to the Tweek Java Programmer's Reference for more comprehensive documentation.

The Tweek Java API is broken up into a collection of Java class libraries, each packaged in a unique JAR file. Programmers can choose the library or libraries they need when writing Java code that uses Tweek. It is possible to write JavaBeans that do not use any of the Tweek Java libraries, but such Beans may not be very full-featured. On the other hand, because the Tweek Java GUI is capable of loading any JavaBean, there is no reason that a fully functional Bean must be written to take advantage of Tweek services and utilities.

GUI Library

The GUI Library contains the heart of the Tweek Java GUI. Indeed, this library is what contains the Tweek Java GUI, and it makes use of all the other Tweek Java libraries to do its job. In general, there is not much code in this library that is of interest to Bean developers. The lone exception is the singleton org.vrjuggler.tweek.gui.MessagePanel. This class provides access to the message panel at the bottom of the Tweek Java GUI layout. Tweek Bean authors can make use of this singleton to post messages to that panel for users of the Tweek GUI to see. In most cases, this should be the preferred method of printing status messages rather than using System.out.print*() or System.err.print*() simply because users of the GUI may not have easy access to the console to see the messages printed there. The GUI Library is found in $TWEEK_BASE_DIR/share/tweek/java/Tweek.jar.

Bean Library

The Bean Library is provided to help simplify the process of loading and managing JavaBeans. The internal handling of JavaBeans performed by the Tweek Java GUI uses classes in this library to communicate with other objects when Beans are loaded, instantiated, removed, etc. The Bean Library is found in $TWEEK_BASE_DIR/share/tweek/java/TweekBeans.jar.

Event Library

The Event Library is used by the GUI Library (i.e., by the Tweek Java GUI) to inform Panel Beans about GUI events. This includes events such as GUI iconification, GUI closing, and the gaining and losing of focus by the GUI. The Event Library is found in $TWEEK_BASE_DIR/share/tweek/java/TweekEvents.jar.

An interesting part of the Event Library is the Event Listener Registry. Historically, the Tweek Java GUI has performed automatic, run-time registration of Beans as event listeners based on the interface(s) that the Beans implement. That is not always convenient, however. For example, a Panel Bean author may want to use utility classes as event listeners rather than the Panel Bean itself. In this case, the utility classes can be registered manually with the Tweek Java GUI using the Event Listener Registry.

Network Library

The Network Library abstracts the use of CORBA by the Tweek Java GUI[2]. It includes the CORBA Service class that handles all ORB and remote reference management, and it has all the Java classes generated by the IDL-to-Java compiler when the Tweek Java API was compiled. Beyond this, it includes the event listener interface for Tweek Beans that wish to be notified of network connection and disconnection events. The Network Library is found in $TWEEK_BASE_DIR/share/tweek/java/TweekNet.jar.

Bean Delivery Library

The Bean Delivery Library makes use of the Bean Library and the Network Library to allow JavaBeans to be transferred across the network. More specifically, a remote subject, most likely written in C++, can “push” JavaBeans to the Tweek GUI where they can be handled as though they were loaded from the local disk. The Bean Delivery Library is found in $TWEEK_BASE_DIR/share/tweek/java/TweekEventDelivery.jar.



[2] This single library is perhaps the reason that people feel compelled to use the “pre-fab” Tweek Java GUI rather than write their own GUI in the language and widget set of their choice.

Chapter 6. Putting It All Together

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