1.2
Copyright © 2002–2007 Iowa State University
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: 2007-04-18 11:28:09 -0500 (Wed, 18 Apr 2007) $
Table of Contents
List of Examples
main(), Part Imain(), Part IIchooseSubjectManager()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.
Table of Contents
Table of Contents
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.
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.
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.)
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 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.
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.
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, 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.
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.
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.
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.
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++.
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.
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.
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.
Table of Contents
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.
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.
Service Beans
Viewer Beans
Panel Beans
Generic 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 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.
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
menu will be modified to enable the
,
, and
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.
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.
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>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.
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.
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);
};
};
#endifIn 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.
Table of Contents
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.
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,
public tweek::SubjectImpl
public:
CustomSubjectImpl() : mValue(0.0f)
{
15 ;
}
virtual ~CustomSubjectImpl()
{
20 ;
}
virtual float getValue();
25 virtual void setValue(float v);
mymod::CustomSubject_ptr _this()
{
return POA_mymod::CustomSubject::_this();
30 }
private:
float mValue;
};
35
}
#endifHere we declare our parent classes,
| |
These two declarations correspond to the
| |
Overriding the method named
Note the namespaces used on this method. The return
type is |
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.
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>5 #include <CustomSubjectImpl.h>
/** * This application starts the CORBA server for the C++ side 10 * of 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("example", argc, argv) )
{ bool status(false); // 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 ) { 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 ) { 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 (...)
{ 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; }
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”. | |
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
| |
Next, we must initialize the CORBA Manager using
the method
| |
To ensure that no exceptions go uncaught, we
enclose the bulk of |
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.
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) )
{
bool status(false);
// Once the CORBA Manager is initialized, we need to
25 // 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 )
{
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(custom_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.
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 )
{
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 )
{
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;
}After the COBRA Manager has been initialized
successfully, the Tweek Subject Manager must be
created. We do this by invoking the method
| |
Once we have a valid Subject Manager, we must
register subjects with it in order for object
references to be passed out by CORBA. This creates the
servant
to which
| |
Once the servant is created, it is registered with the Subject Manager. The Subject Manager will activate the servant within the POA so that references to it can be created and returned to clients. We register it with the symbolic name “CustomSubject” that can be referenced later by remote objects. | |
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 ' |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Table of Contents
Based on the information presented in the previous chapters, we can combine everything into an examples. In this chapter, we present the step-by-step process for using the Tweek Java and C++ APIs.
In this example, we explain how to develop a simple Tweek
interface. The goal is to have a “collaborative”
slider in a Java GUI component. The value displayed by the slider
is retained by a C++ application so that multiple independent
sliders can show the same value. The steps explained here are
highly representative of the normal steps to be followed when
using the Tweek Java and C++ APIs. The structure of the following
sections lays out the order of the steps taken. An example
makefile that goes along with the code presented can be found in
the section called “SliderSubject” of Appendix A, Compiling Example Code. The full source for the examples
presented in this section can be found in
$TWEEK_BASE_DIR/share/test/NetworkTestBean.
To begin, the subject interface must be defined in IDL and implemented in C++. The interface itself will be “compiled” into Java and C++ code. Both ends of the communication channels must know the interface in order for the references to be used, thus requiring the generation of code for both languages.
Creating an IDL interface involves writing an IDL
file. For this example, we will be storing an integer
variable in a C++ servant. The Java GUIs will need to read
and write the value, so we need two methods:
getValue() and
setValue(). The type being passed
between ORBs will
be long, a 32-bit integer. Depending on the
target language, this will map to the corresponding type of
the same size.
Example 6.1. SliderSubject.idl
1 #ifndef _NETWORK_TEST_SLIDER_SUBJECT_IDL_#define _NETWORK_TEST_SLIDER_SUBJECT_IDL_
#include <tweek/idl/Subject.idl>
5 module networktest
{ interface SliderSubject : tweek::Subject
{ 10 void setValue(in long val);
long getValue();
}; }; 15 #endif
![]()
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. | |
All Tweek subject IDL files must include
| |
Typically, using a module to contain the
interface is recommended. Using a module defines a
Java package and a C++ namespace (named
| |
The definition of the subject interface must
inherit from
| |
This simple interface has only two functions:
|
The file SliderSubject.idl must
be “compiled” by an IDL compiler. For
use with Tweek, the interface must be compiled into Java and
C++ code. The generated Java code will be used solely for
communicating with CORBA
networktest.SliderSubject
references. The generated C++ code will be extended to
provide an implementation of the
networktest::SliderSubject
interface. (The implementation will be a CORBA servant object to which
references will be made by remote Java code.)
After running an IDL compiler to generate the stub
CORBA code, the interface must be implemented. In
particular, there will be pure virtual methods in
SliderSubject.h that must be
implemented. The implementing class will be the CORBA
servant
holding the actual data visualized in the Java GUI
slider.
Example 6.2. SliderSubjectImpl.h
1 #ifndef _SLIDER_SUBJECT_IMPL_H_ #define _SLIDER_SUBJECT_IMPL_H_ #include <tweek/tweekConfig.h> 5 #include <vector> #include <tweek/CORBA/SubjectImpl.h>#include <SliderSubject.h>
10 namespace networktest
{ /** 15 * This class is an extension to the base Tweek SubjectImpl class. * It uses multiple inheritance with that class and with the * generated CORBA class corresponding to the IDL for * SliderSubject. */ 20 class SliderSubjectImpl : public POA_networktest::SliderSubject
, public tweek::SubjectImpl { public: 25 SliderSubjectImpl() : tweek::SubjectImpl(), mValue(0) { /* Do nothing. */ ; } 30 virtual ~SliderSubjectImpl() { /* Do nothing. */ ; } 35 /** * Sets this subject's internal value. */ virtual void setValue(long value);
40 /** * Returns this subject's internal value. */ virtual long getValue();
45 /** * This overriding method is needed so that the correct type * is returned when the _this() method is invoked. Without * this method, an object of type tweek::Subject_ptr would 50 * be returned. * * XXX: It may be possible to remove this requirement in * the future. */ 55 networktest::SliderSubject_ptr _this()
{ return POA_networktest::SliderSubject::_this(); } 60 private: long mValue; /**< Our value */
}; } // End of networktest namespace 65 #endif /* _SLIDER_SUBJECT_IMPL_H_ */
These files will always be included by
implementations of Tweek subject derived classes.
The first contains the declaration for the basic
Tweek subject implementation. The second contains
the C++ code generated from the Tweek
| |
This header is generated by the IDL compiler
from | |
In | |
The interface implementation class must
inherit from the IDL-generated class
| |
| |
As of this writing, all subject
implementations must contain an overriding version
of the | |
This is the actual value being stored by the C++ servant. |
In SliderSubjectImpl.h, the most
important parts to note are the use of multiple inheritance,
the declarations of the
SliderSubject interface
methods, and the implementation of
_this(). The implementations of
setValue() and
getValue() are shown next in Example 6.3, “SliderSubjectImpl.cpp”.
Example 6.3. SliderSubjectImpl.cpp
1 #include <vpr/Util/Debug.h> #include <SliderSubjectImpl.h>namespace networktest 5 { void SliderSubjectImpl::setValue(long value) { mValue = value;
10 // Notify any observers that our value has changed. This is very // important. tweek::SubjectImpl::notify();
} 15 long SliderSubjectImpl::getValue() { return mValue;
} 20 } // End networktest namespace
Include the class declaration file, shown in Example 6.2, “SliderSubjectImpl.h”. | |
When invoked, the remote caller will pass a long value, and this saves the result into the servant's storage. | |
Because the subject's state has been modified,
all attached observers must be notified. This is a
very important step that must be taken in this
method. Note that it invokes the
| |
Observers will invoke this method when
requesting the current
|
The observer does not define its own specialized IDL
interface. Instead, it makes use of the existing Tweek basic
observer (tweek.ObserverPOA in Java). The method
update() must be implemented. The
remainder of the observer implementation is centered around
communication with a
SliderSubject object reference.
All observer code is written in Java. The only C++ code for
observers is part of the Tweek library, and it is generated
entirely by the IDL
compiler.