1.0
Copyright © 2002–2005 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: 2006-11-15 10:24:10 -0600 (Wed, 15 Nov 2006) $
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
}
#endif | Here 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).success() )
{ 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 (...)
{ 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).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();
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(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.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 )
{
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
![]()
The file SliderSubject.idl must be
“compiled” by an IDL compiler. For use
with Tweek, the interface must be compiled into Java and C++ code.
The generated Java code will be used solely for communicating with
CORBA networktest.SliderSubject
references. The generated C++ code will be extended to provide an
implementation of the
networktest::SliderSubject
interface. (The implementation will be a CORBA servant object to which
references will be made by remote Java code.)
After running an IDL compiler to generate the stub CORBA
code, the interface must be implemented. In particular, there will
be pure virtual methods in SliderSubject.h
that must be implemented. The implementing class will be the CORBA
servant holding the
actual data visualized in the Java GUI slider.
Example 6.2. SliderSubjectImpl.h
1 #ifndef _SLIDER_SUBJECT_IMPL_H_ #define _SLIDER_SUBJECT_IMPL_H_ #include <tweek/tweekConfig.h> 5 #include <vector> #include <tweek/CORBA/SubjectImpl.h>#include <SliderSubject.h>
10 namespace networktest
{ /** 15 * This class is an extension to the base Tweek SubjectImpl class. * It uses multiple inheritance with that class and with the * generated CORBA class corresponding to the IDL for * SliderSubject. */ 20 class SliderSubjectImpl : public POA_networktest::SliderSubject
, public tweek::SubjectImpl { public: 25 SliderSubjectImpl() : tweek::SubjectImpl(), mValue(0) { /* Do nothing. */ ; } 30 virtual ~SliderSubjectImpl() { /* Do nothing. */ ; } 35 /** * Sets this subject's internal value. */ virtual void setValue(long value);
40 /** * Returns this subject's internal value. */ virtual long getValue();
45 /** * This overriding method is needed so that the correct type * is returned when the _this() method is invoked. Without * this method, an object of type tweek::Subject_ptr would 50 * be returned. * * XXX: It may be possible to remove this requirement in * the future. */ 55 networktest::SliderSubject_ptr _this()
{ return POA_networktest::SliderSubject::_this(); } 60 private: long mValue; /**< Our value */
}; } // End of networktest namespace 65 #endif /* _SLIDER_SUBJECT_IMPL_H_ */
| These files will always be included by implementations
of Tweek subject derived classes. The first contains the
declaration for the basic Tweek subject implementation. The
second contains the C++ code generated from the Tweek
|
| This header is generated by the IDL compiler from
|
| In |
| The interface implementation class must inherit from
the IDL-generated class
|
|
|
| As of this writing, all subject implementations must
contain an overriding version of the
|
| This is the actual value being stored by the C++ servant. |
In SliderSubjectImpl.h, the most
important parts to note are the use of multiple inheritance, the
declarations of the SliderSubject
interface methods, and the implementation of
_this(). The implementations of
setValue() and
getValue() are shown next in Example 6.3, “SliderSubjectImpl.cpp”.
Example 6.3. SliderSubjectImpl.cpp
1 #include <vpr/Util/Debug.h> #include <SliderSubjectImpl.h>namespace networktest 5 { void SliderSubjectImpl::setValue(long value) { mValue = value;
10 // Notify any observers that our value has changed. This is very // important. tweek::SubjectImpl::notify();
} 15 long SliderSubjectImpl::getValue() { return mValue;
} 20 } // End networktest namespace
| Include the class declaration file, shown in Example 6.2, “SliderSubjectImpl.h”. |
| When invoked, the remote caller will pass a long value, and this saves the result into the servant's storage. |
| Because the subject's state has been modified, all
attached observers must be notified. This is a very
important step that must be taken in this method. Note that
it invokes the |
| Observers will invoke this method when requesting the
current |
The observer does not define its own specialized IDL
interface. Instead, it makes use of the existing Tweek basic
observer (tweek.ObserverPOA in Java). The method
update() must be implemented. The remainder
of the observer implementation is centered around communication with
a SliderSubject object reference. All
observer code is written in Java. The only C++ code for observers is
part of the Tweek library, and it is generated entirely by the
IDL
compiler.
For every subject interface defined in IDL, a corresponding observer class must be written in Java. Without an observer, there is no way for the Java and C++ sides to conduct useful two-way communication. At best, the Java GUI could request a subject reference and manipulate the C++ application through the reference, but the communication would be entirely one-way.
In Example 6.4, “SliderObserverImpl.java”, we
show the complete Java implementation of an observer corresponding
to the tweek::SliderSubject
interface defined earlier. (The JavaBean that uses this observer
is explained in the section called “The JavaBean”.)
The main focus of this observer is to update its contained
JSlider whenever the state of the
corresponding subject changes.
Example 6.4. SliderObserverImpl.java
1 package networktest; import javax.swing.DefaultBoundedRangeModel; import javax.swing.JSlider; 5 import tweek.*; /** * Implementation of the Observer side of the Tweek * Subject/Observer pattern. It must extend tweek.ObserverPOA 10 * so that instances of this class can be registered as CORBA * servants. In addition, CORBA references to the servants must * be capable of being attached to remote subjects. */ public class SliderObserverImpl extends ObserverPOA15 { public SliderObserverImpl(JSlider slider,
SliderSubject subject) { mSlider = slider; 20 mSliderSubject = subject; } /** * Implements the required method in tweek.ObserverPOA. The 25 * remote subject will invoke this method whenever it is * notified of a change. */ public void update()
{ 30 // If we have a valid slider object, we need to update // its value to whatever our subject has. if ( mSlider != null ) { DefaultBoundedRangeModel model = 35 (DefaultBoundedRangeModel) mSlider.getModel(); model.setValue(mSliderSubject.getValue());
mSlider.repaint(); } } 40 /** * Detaches this observer from our subject. This is needed * when shutting down a CORBA connection. */ 45 public void detach() { mSliderSubject.detach(this._this());
} 50 private SliderSubject mSliderSubject = null; private JSlider mSlider = null; }
| As an observer, this class must derive from
|
| The constructor for this observer takes two arguments:
a |
| As stated, all observers must implement
|
| To get the updated state of the remote subject, the
encapsulated subject reference's
|
| It may be convenient for the observer to implement a
|
This example demonstrates that observers do not have to be complex to be usable. While this example is purposely simple, it should illustrate that developers of observers do not necessarily have to make their implementations complicated. As will be shown in the section called “The JavaBean”, the JavaBean that uses this observer completes the picture and provides users with a GUI slider that can be manipulated by any number of simultaneous users.
Now that we have the subject and observer ready to go, we can
make an application that uses them. The following example is a
(relatively) simple C++ application that starts the CORBA
Manager, creates the Subject Manager, registers a
networktest::SliderSubject servant,
and then waits for the user to press 'x' to exit.
The use of exceptions may appear unfamiliar to some C++ programmers.
CORBA makes use of exceptions as a cross-language mechanism to
report errors, and thus, there must be proper exception handling
code for the application to work correctly.
Example 6.5. SliderSubjectApp.cpp
1 #include <tweek/CORBA/CorbaManager.h>#include <vpr/Thread/Thread.h> #include <vpr/Util/Debug.h> 5 #include <SliderSubjectImpl.h>
/** * This application starts the CORBA server for the C++ side * of the test. 10 */ int main(int argc, char* argv[]) { tweek::CorbaManager mgr;
15 // The first thing we have to do is initialize the Tweek // CORBA Manager. If this fails, we're out of luck. try { if ( mgr.init("corba_test", argc, argv).success() )
20 { vpr::ReturnStatus status; // Once the CORBA Manager is initialized, we need to // create a Subject Manager. This will hold our 25 // SliderSubject object. try { status = mgr.createSubjectManager();
30 // If we were able to create the Subject Manager, // now we register our objects with it. if ( status.success() ) { // First, create real instances of the C++ 35 // object that will be the CORBA servant. This // must be allocated on the heap. networktest::SliderSubjectImpl* slider_subj = new networktest::SliderSubjectImpl();
40 // Now we try to register the subject and give // it a symbolic, easy-to-remember name. try { mgr.getSubjectManager()-> 45 registerSubject(slider_subj, "SliderSubject");
} catch (...) { 50 vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Failed to register subject\n" << vprDEBUG_FLUSH; } 55 // We are done with our pointer to the servant. slider_subj->_remove_ref(); } } catch (CORBA::Exception& ex) 60 { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Caught an unknown CORBA exception when " << "trying to register!\n" << vprDEBUG_FLUSH; 65 } if ( ! status.success() ) { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 70 << "Failed to register Subject Manager instance\n" << vprDEBUG_FLUSH; } std::cout << "Press 'x' to exit" << std::endl; 75 char input; // Loop forever so that we can act sort of like // a server. while ( 1 )
80 { std::cin >> input; if ( input == 'x' ) { break; 85 } else { vpr::System::msleep(100); } 90 } } else { vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 95 << "CORBA failed to initialize\n" << vprDEBUG_FLUSH; } } catch (...) { 100 vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) << "Caught an unknown exception!\n" << vprDEBUG_FLUSH; } vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL) 105 << "Exiting\n" << vprDEBUG_FLUSH; return 0; }
| These two headers are typically needed. The first includes the declaration of the Tweek CORBA Manager, and the second is the subject implementation declaration, shown in Example 6.2, “SliderSubjectImpl.h”. |
| In order to use CORBA through Tweek, the CORBA Manager must be created and initialized. Any number of these may be created, but in general, only one is needed per application |
| After the COBRA Manager has been initialized successfully, the Tweek Subject Manager must be created. |
| Once we have a valid Subject Manager, we must register
subjects with it in order for object references to be passed
out by CORBA. This creates the servant to which
|
| Once the servant is created, it is registered with the Subject Manager. The Subject Manager will activate the servant within the POA so that references to it can be created and returned to clients. |
| After the subject is registered, all the work is done.
This application now just waits for clients to request
references. It will exit when the user enters
' |
The application shown in Example 6.5, “SliderSubjectApp.cpp” is purposely simple. There are many ways to use the CORBA Manager and the Subject Manager. For example, an object registry could be built on top of the Subject Manager so that only specific types of servant objects may be registered. Servant registration could be automated in object constructors. The C++ API is intended to be simple to enhance its usability and flexibility, and the application shown in the example is just that: an example.
We have finally reached the point at which we implement a JavaBean that can visualize the numeric data held by the C++ slider subject. Such a JavaBean must be defined as a Tweek Panel Bean. (Refer back to Chapter 2, JavaBeans if these statements seem unfamiliar or confusing.)
The JavaBean shown in the following example is typical of one
that uses the Tweek Network library to take advantage of CORBA
facilities. The code for the Bean is longer than previous examples,
and because of this, it will be split into multiple code blocks.
Each will be discussed in turn. The full code is in one file:
NetworkTest.java.
Please note that the details of setting up the GUI elements used by the Bean are left out. The code in this case was generated by JBuilder and could easily vary from Bean to Bean. For the full code, please refer to the aforementioned locations.
The slider JavaBean uses common Java Swing classes, a CORBA exception class, the Tweek Event library, the Tweek Network library, and the Java code generated by an IDL compiler. The following explains how each of these are imported into the main Bean class.
package networktest;import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import org.omg.CORBA.BAD_PARAM;
import org.vrjuggler.tweek.event.*;
import org.vrjuggler.tweek.net.*;
import org.vrjuggler.tweek.net.corba.*;
import tweek.*;
![]()
Now we can begin writing the Bean code. Besides the class
SliderObserverImpl, this Bean has only one
public class: NetworkTest. It will provide
the GUI representation of the numeric data. The declaration of the
class and its package-visible helper follow.
/** * This is an example of a JavaBean that Tweek can load dynamically. It holds * a JSlider that acts as an Observer in the Tweek CORBA Subject/Observer * pattern implementation. * * @version 1.0 */ public class NetworkTest extends JPanelimplements CommunicationListener
{ ... } class FrameListener extends TweekFrameAdapter
{ ... }
Before delving into the methods of the
networktest.NetworkTest class, it will be
helpful to review the member variables used throughout the class.
Refer back to this section if there is any confusion regarding the
use or the type of some member variable in the method
implementations.
private BorderLayout mBeanLayout = new BorderLayout();private JPanel mSliderPanel = new JPanel();
private JSlider mDataSlider = new JSlider();
private SliderObserverImpl mSliderObserver = null;
![]()
Note that the observer is stored in a member variable initialized to null. It will be assigned a value when a CORBA service becomes available and the corresponding subject can be requested. The object itself is stored as a member variable so that it can be accessed by all the methods of the class.
The most complex part of this Bean is the handling of CORBA
communication events delivered by the Tweek GUI. All of the
handling in this example will be done in the methods
connectionOpened() and
connectionClosed(). The steps that must
be followed are straightforward, but there are errors that must be
handled properly. It is the error handling that can make the code
look daunting, not the use of the Tweek Network library.
1 /** * Implements the Tweek CommunicationListener interface needed * for being informed of new connections with remote ORBs. */ 5 public void connectionOpened(CommunicationEvent e){ // The first thing to do is get the CORBA service object from // the event. We need this so we know to whom we are are // connecting. Once we have the CORBA service, we get its 10 // Subject Manager since that's what contains the actual // subjects we need. CorbaService corba_service = e.getCorbaService();
SubjectManager mgr = corba_service.getSubjectManager();
15 Subject subject = mgr.getSubject("SliderSubject");
SliderSubject slider_subject = null; // Try to narrow the Subject object to a SliderSubject object. // If this fails, it throws a CORBA BAD_PARAM exception. In 20 // that case, we open a dialog box saying that the narrowing // failed. try { slider_subject = SliderSubjectHelper.narrow(subject);
25 } catch (BAD_PARAM narrow_ex)
{ JOptionPane.showMessageDialog( null, 30 "Failed to narrow subject to SliderSubject", "SliderSubject Narrow Error", JOptionPane.ERROR_MESSAGE ); } 35 // Ensure that slider_subject is a valid object just to be // safe. if ( slider_subject != null ) { 40 // First, we need a Java object that implements the // Observer. That object must be registered with the Java // CORBA service. mSliderObserver = new SliderObserverImpl(mDataSlider, slider_subject);
45 corba_service.registerObject(mSliderObserver, "SliderObserver");
// Now that the observer is registered, we can attach it // to the subject. The subject needs to know who its 50 // observers are so that it can notify them of updates. slider_subject.attach(mSliderObserver._this());
// Now we set the slider in our GUI to be whatever value // the remote subject is holding for us. 55 mDataSlider.setValue(slider_subject.getValue());
mDataSlider.addChangeListener( new SliderChangeListener(slider_subject) );
} 60 } /** * Implements the Tweek CommunicationListener interface needed * for being informed when existing ORB connections are closed. 65 */ public void connectionClosed(ConnectEvent e)
{ if ( mSliderObserver != null ) { 70 mSliderObserver.detach();
mSliderObserver = null; } }
It is important to note that the use of hard-coded subject names is not recommended. In this example, the subject name is hard-coded for simplicity. The Tweek Subject Manager allows accessing code to request a list of all registered subjects. Using this information, it is possible to present the Java GUI user with a list of subjects from which they can make a selection.
This Bean makes an effort to shut down open CORBA
connections when the Tweek GUI is closed. It does this through a
helper class that extends
org.vrjuggler.tweek.event.TweekFrameAdapter,
overriding only one method:
frameClosing(). This method calls back
into the NetworkTest class, invoking its own
frameClosing() method. For more
information about the other Tweek GUI frame events delivered
through the interface
org.vrjuggler.tweek.event.TweekFrameListener,
refer to the Tweek Java API reference.
public class NetworkTest
extends JPanel
implements CommunicationListener
{
...
public boolean frameClosing()
{
disconnect();
return true;
}
...
}
class FrameListener extends TweekFrameAdapter
{
public FrameListener(NetworkTest bean)
{
this.bean = bean;
}
public boolean frameClosing(TweekFrameEvent e)
{
return bean.frameClosing();
}
private NetworkTest bean = null;
}For the helper class FrameListener to
be notified of Tweek GUI frame events, it must be registered with
the Tweek event listener registry (a singleton). This step is
performed in the NetworkTest constructor,
as shown below.
public NetworkTest()
{
try
{
jbInit();
}
catch(Exception e)
{
e.printStackTrace();
}
mFrameListener = new FrameListener(this);
EventListenerRegistry.instance().
registerListener(mFrameListener,
TweekFrameListener.class);
}A helper class is used to handle local change events in the slider. The class is a private inner class within the Bean and is defined as follows:
private class SliderChangeListener implements ChangeListener
{
public SliderChangeListener(SliderSubject subject)
{
mSliderSubject = subject;
}
public void stateChanged(javax.swing.event.ChangeEvent e)
{
JSlider source = (JSlider) e.getSource();
if ( ! source.getValueIsAdjusting() )
{
mSliderSubject.setValue(source.getValue());
}
}
private SliderSubject mSliderSubject = null;
}With the Bean implementation done, the XML file that describes the Bean must be written. All Beans loaded by the Tweek Java GUI are described by an XML file. In Example 6.6, “NetworkTestBean.xml”, we show the XML file for the Bean we have been developing.
Example 6.6. NetworkTestBean.xml
<?xml version="1.0" encoding="UTF-8"?><beanlist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.vrjuggler.org/tweek/xsd/1.1/beanlist.xsd"> <guipanel name="Network Tester">
<file
name="${TWEEK_BASE_DIR}/share/tweek/beans/NetworkTestBean.jar" class="networktest.NetworkTest" /> <tree path="/Beans" />
</guipanel> </beanlist>
After the Bean is compiled into a JAR file, the JAR file and
XML file need to be copied into
$TWEEK_BASE_DIR/share/tweek/beans. This is
the default path that Tweek searches for Beans at startup, and it
is a convenient place to put this example Bean.
With all the coding done and the code compiled, we can run the C++ application and connect multiple instances of the Tweek Java GUI to it. The steps to run the C++ application are as follows:
Run a CORBA Naming Service. This is required so that the C++ and Java ORBs can resolve symbolic references.
Since Tweek uses omniORB for a C++ ORB (see Appendix B, CORBA Implementations for more information on this),
the environment variable $OMNIORB_CONFIG must be
set. This gives the full path to an omniORB configuration file
that omniORB will load at runtime.
Run the C++ application.
Run the Tweek Java GUI. This will find and load all the
Beans in
$TWEEK_BASE_DIR/share/tweek/beans.
Within the Java GUI, connect to the CORBA Naming Service started in step 1. The way the Bean is written, it will request the subject held by the C++ application automatically, and the slider will be updated to the current value.
Not everything written in Tweek must use Java, C++, IDL, and CORBA. The Tweek Java GUI is a generalized Bean-loading environment, and as such, its main focus is to load JavaBeans and present them to the user. Those Beans may take advantage of CORBA, but they are not required to do so. In this section, we show another sample Bean that is much simpler than the previous example.
The Bean shown in this example can open and close multiple text
files. To do this, the Bean implements the interface
org.vrjuggler.tweek.beans.FileLoader.
The Java code will be shown in separate pieces to make it easier to
understand. The full code is in
$TWEEK_BASE_DIR/share/test/FileOpenTestBean/fileopentestbean/FileOpenTestBean.java.
An example makefile can be found in the section called “File Loader”. within Appendix A, Compiling Example Code..
As in the previous example, we focus on the imported classes first. They are as follows:
package fileopentestbean;import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import javax.swing.*;
import org.vrjuggler.tweek.services.ExtensionFileFilter;
import org.vrjuggler.tweek.beans.FileLoader;
![]()
Next, we show the class declaration and the member variables.
public class FileOpenTestBean
extends JPanel
implements java.io.Serializable, FileLoader
{
// Methods ...
private int openFileCount = 0;
private BorderLayout mMainLayout = new BorderLayout();
private JLabel mBeanTitle = new JLabel();
private JTabbedPane mTextContainer = new JTabbedPane();
}Now that we have the basics for the class, we can start
implementing the FileLoader
interface. Of the methods that must be implemented, the most complex
is openRequested(). It will be shown last
because of its length. We will begin instead with the simplest
methods.
1 public String getFileType()
{
return "Text";
}
5
public boolean canOpenMultiple()
{
return true;
}
10
public boolean canSave()
{
return false;
}
15
public boolean saveRequested()
{
return false;
}
20
public boolean closeRequested()
{
mTextContainer.remove(
mTextContainer.getSelectedComponent()
25 );
openFileCount--;
return true;
}
30 public int getOpenFileCount ()
{
return openFileCount;
}The above are all the methods of the
FileLoader interface except
openRequested(). We are now ready to move
on to it.
1 public boolean openRequested()
{
// Initialize this to false since a lot of things can go wrong
// in the process of opening files. Once the file is opened
5 // and read successfully, this can be changed to true.
boolean opened = false;
JFileChooser chooser = new JFileChooser();
chooser.setMultiSelectionEnabled(false);
10 chooser.setDialogTitle("Text File Loader");
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
// Only load .txt files.
ExtensionFileFilter filter =
15 new ExtensionFileFilter("Text Files");
filter.addExtension("txt");
chooser.addChoosableFileFilter(filter);
int status = chooser.showOpenDialog(this);
20
if ( status == JFileChooser.APPROVE_OPTION )
{
File file = chooser.getSelectedFile();
25 if ( file.canRead() )
{
try
{
// Read the contents of the file into a byte[]
30 // object.
FileInputStream input_file =
new FileInputStream(file);
byte[] file_data = new byte[(int) file.length()];
input_file.read(file_data);
35
// Create a text area to hold the contents of the
// file.
JTextArea text_area = new JTextArea();
text_area.setEditable(false);
40 text_area.insert(new String(file_data), 0);
// Create a scroll pane to hold the text area; add
// it to the tabbed pane with all the other
// previously loaded scroll panes; and make the new
45 // scroll pane the selected component.
JScrollPane text_comp = new JScrollPane(text_area);
mTextContainer.add(text_comp, file.getName());
mTextContainer.setSelectedComponent(text_comp);
50 // Our work is done.
openFileCount++;
opened = true;
}
catch (java.io.FileNotFoundException ex)
55 {
JOptionPane.showMessageDialog(
null,
"Cannot find '" + file.getAbsolutePath() + "'",
"Read Error", JOptionPane.ERROR_MESSAGE
60 );
}
catch (java.io.IOException ex)
{
JOptionPane.showMessageDialog(
65 null,
"Error reading from '" + file.getAbsolutePath() +
"':" + ex.getMessage(),
"Read Error", JOptionPane.ERROR_MESSAGE
);
70 }
}
else
{
JOptionPane.showMessageDialog(
75 null,
"Cannot read from file '" +
file.getAbsolutePath() + "'",
"Read Error", JOptionPane.ERROR_MESSAGE
);
80 }
}
return opened;
}This Bean uses no CORBA code and does not require C++ code to act as a peer. This may be the case for many Panel Beans written for Tweek. Of course, this Bean could be extended to open files that are then handed off to C++ code through CORBA.
The XML file for this Bean is very simple. It simply lists the Bean file information and puts the Bean at the root of the Bean tree. The full file is shown in Example 6.7, “FileOpenTestBean.xml”.
Example 6.7. FileOpenTestBean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beanlist
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.vrjuggler.org/tweek/xsd/1.1/beanlist.xsd">
<guipanel name="File Open Test Bean">
<file
name="${TWEEK_BASE_DIR}/share/tweek/beans/FileOpenTestBean.jar"
class="fileopentestbean.FileOpenTestBean" />
<tree path="/" />
</guipanel>
</beanlist>Table of Contents
As of Tweek version 0.13.0, users can write C++ clients in
addition to Java and Python clients. As of this writing, the interface
is relatively low-level. The C++ counterpart of the Java class
org.vrjuggler.tweek.net.corba.CorbaService is used to communicate with a remote C++ server. The
class is tweek::CorbaService, and its API is almost identical to the Java version. The
basic idea is that users familiar with the Java API can easily make use
of the C++ version and vice versa.
In this chapter, we explain how to write C++ clients. First, we cover the CORBA Service, and then we show how to use it with a C++ client application. The basic ideas are identical to those used in Java clients. The syntax varies slightly due to the differences in the C++ and Java mappings of IDL. Additionally, we do not need to be concerned with JavaBeans because we will be speaking strictly in terms of C++. There is nothing to prevent an ambitious user from developing a C++ counterpart to the Tweek Java GUI, however.
The CORBA Service, implemented in the class
tweek::CorbaService, simplifies the use of CORBA for C++ clients. Instances
are constructed by providing the host name and port where the CORBA
Naming Service is listening for incoming requests. Once constructed,
the CORBA Service must be initialized. This creates the local root
POA, acquires a reference to the Naming Service, and starts the local
ORB thread. At this point, the CORBA Service can be used exactly as it
would be used in Java (see Chapter 6, Putting It All Together). For
example, the remote Subject Manager reference can be requested and
servants (C++ implementations of
tweek::Observer) can be
registered.
To understand how to use the C++ client API, we will examine a simple application. Similar to the Java/C++ application we reviewed in Chapter 6, Putting It All Together, this application makes use of an IDL-specified interface, and a C++ server. The key differences, then, are that there is no Java code in this case, and we will not need to use XML.
All the code shown here, including a GNU makefile, can be found
in the directory
$TWEEK_BASE_DIR/share/tweek/test/CxxClient. In
the following sections, we will refer to the specific files and
highlight key sections within them. We assume that readers have
already read and understood Chapter 6, Putting It All Together. This
is important because we will refer back to concepts illustrated in
that chapter. Further, we will not present the server code in this
chapter because it is nearly identical to the code for the server
discussed previously. Interested readers can review the file
server.cpp.
As usual, we begin by defining an interface. For this example,
we will use an interface that provides access to a simple string
object. The server (Subject) will maintain the value of the string,
and the clients (Observers) will be able to query and manipulate the
string. The interface will be called
CxxClientTest::StringSubject. The
code for the interface is shown in Example 7.1, “StringSubject.idl”.
Example 7.1. StringSubject.idl
Now that we have our interface defined abstractly, we must
provide a C++ implementation of the Subject. This will be done in
the files StringSubjectImpl.cpp and
StringSubjectImpl.h. We begin with the header
file, shown in Example 7.2, “StringSubjectImpl.h”. While
the example code may appear long, there is only slight variation
from previous Subject implementation header files. We highlight the
important bits, of course.
Example 7.2. StringSubjectImpl.h
1 #ifndef _STRING_SUBJECT_IMPL_H_ #define _STRING_SUBJECT_IMPL_H_ #include <tweek/tweekConfig.h> 5 #include <string> #include <vpr/vpr.h> #include <vpr/Sync/Mutex.h> 10 #include <tweek/CORBA/SubjectImpl.h> #include <StringSubject.h>namespace CxxClientTest { 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 20 * StringSubject. */ class StringSubjectImpl : public POA_CxxClientTest::StringSubject
, public tweek::SubjectImpl 25 { public: StringSubjectImpl() : tweek::SubjectImpl(), mValue("") { /* Do nothing. */ ; 30 } virtual ~StringSubjectImpl() { /* Do nothing. */ ; 35 } /** * Sets this subject's internal value. */ 40 virtual void setValue(const char* value);
/** * Returns this subject's internal value. */ 45 virtual char* getValue();
/** * This overriding method is needed so that the correct * type is returned when the _this() method is invoked. 50 * Without this method, an object of type tweek::Subject_ptr * would be returned. */ CxxClientTest::StringSubject_ptr _this()
{ 55 return POA_CxxClientTest::StringSubject::_this(); } private: std::string mValue; /**< Our value */
60 /** A mutex to protect mValue accesses */ vpr::Mutex mValueLock;
}; 65 } // End of CxxClientTest namespace #endif /* _STRING_SUBJECT_IMPL_H_ */
Next, we look at the very simple implementations of
CxxClientTest::StringSubjectImpl::setValue()
and
CxxClientTest::StringSubjectImpl::getValue().
These are found in the file
StringSubjectImpl.cpp, and the code is shown in
Example 7.3, “StringSubjectImpl.cpp”. It is important to
note the use of guards in the method implementations. These provide
an exception-safe mechanism for controlling access to the data
member mValue. When constructed, the guard locks
the mutex passed as the argument to the constructor. When the guard
goes out of scope, the mutex is automatically unlocked.
Example 7.3. StringSubjectImpl.cpp
1 #include <vpr/Sync/Guard.h> #include <StringSubjectImpl.h>namespace CxxClientTest 5 { void StringSubjectImpl::setValue(const char* value) { { 10 vpr::Guard<vpr::Mutex> val_guard(mValueLock);
mValue = std::string(value);
} // Notify any observers that our value has changed. This 15 // is very important. tweek::SubjectImpl::notify();
} char* StringSubjectImpl::getValue() 20 { vpr::Guard<vpr::Mutex> val_guard(mValueLock);
return CORBA::string_dup(mValue.c_str());
} 25 } // End CxxClientTest namespace
With the Subject implemented, we now turn our attention to a
C++ Observer implementation. Its implementation we will be very
simple. Its update() method will query the
current string value and print it to the console. The goal here is
to demonstrate how to write a C++ Observer, not how to write an
interesting Observer.
Example 7.4. StringObserverImpl.h
1 #ifndef _STRING_OBSERVER_IMPL_H_ #define _STRING_OBSERVER_IMPL_H_ #include <tweek/CORBA/Observer.h>5 #include <StringSubject.h>
class StringObserverImpl : public POA_tweek::Observer
{ public: 10 StringObserverImpl(CxxClientTest::StringSubject_var subject) : mSubject(subject) { /* Do nothing. */ ; } 15 virtual ~StringObserverImpl() { /* Do nothing. */ ; } 20 virtual void update();
void detach() { 25 mSubject->detach(_this());
} private: CxxClientTest::StringSubject_var mSubject;
30 }; #endif /* _STRING_OBSERVER_IMPL_H_ */
| First, we include the headers we need. The first
declares the basic |
| Our Observer implementation derives from the basic Tweek
Observer class |
| The |
| The |
| Finally, we have a member variable that we will use to
retain a reference our Subject. This allows the Observer to be
passed around to functions without passing its Subject
explicitly. Further, we will see that having a reference to
the Subject is required for the
|
Now, we show the implementation of
StringObserverImpl::update(). This method
is implemented in StringObserverImpl.cpp. While
the method body is very short, we use the .cpp
file to encourage the implementation of methods outside of the class
declaration.
Example 7.5. StringObserverImpl.cpp
#include <iostream> #include <StringObserverImpl.h>void StringObserverImpl::update() { char* cur_value = mSubject->getValue();
std::cout << "Current string value is now '"
<< cur_value << "'" << std::endl; delete cur_value;
}
Finally, we look at the C++ client application. This brings
everything together and makes use of the
tweek::CorbaService class to contact the
remote server. The application shown below is more complicated than
the server because more work must done. We must initialize the local
CORBA Service (the local ORB); we must pick out the correct Subject
Manager reference; and we must get the correct
CxxClientTest::StringSubject
reference from the Subject Manager. Once all of those steps are
completed, we can create an Observer servant (an instance of
StringObserverImpl) and attach it to the
remote Subject.
The file containing the complete client application source is
client.cpp. We will examine it in three parts: the required headers,
the implementation of main(), and the Subject
Manager lookup.
Example 7.6. client.cpp: Required Header Files
#include <iostream> #include <string> #include <vpr/vpr.h> #include <vpr/Util/Debug.h> #include <tweek/Client/CorbaService.h>#include <tweek/CORBA/SubjectManager.h>
#include <StringObserverImpl.h>
![]()
Next, we look at the implementation of the application's
main() function. This is where the bulk of the
work is done. Of course, in real-world use, modularizing the code
would be much better than dumping most of it in
main(). For the purposes of this example, we
can get by with having most of the code in
main().
Note the use of try/catch blocks in the client application code. As in the case of server applications (refer to the section called “The Server Application”), we are careful about handling exceptions properly. Remember that CORBA uses exceptions extensively to indicate errors, and thus it is necessary for user code to catch them.
Example 7.7. client.cpp: Implementation of main(),
Part I
1 int main(int argc, char* argv[])
{
std::string ns_host, iiop_ver;
vpr::Uint16 ns_port;
5
std::cout << "Naming Service host: ";
std::cin >> ns_host;
std::cout << "Naming Service port (usually 2809): ";
10 std::cin >> ns_port;
std::cout << "IIOP version (usually 1.0): ";
std::cin >> iiop_ver;
15 // Create the local CORBA Service using the Naming Service
// URI information we just collected.
tweek::CorbaService corba_service(ns_host, ns_port,
iiop_ver);
20 try
{
// Attempt to initialize the CORBA Service.
if ( corba_service.init(argc, argv).success() )
{
25 // This will hold the reference to the Subject Manager
// we use.
tweek::SubjectManager_var subj_mgr =
chooseSubjectManager(corba_service);
30 // Verify that we actually got a Subject Manager
// reference back from chooseSubjectManager.
if ( ! CORBA::is_nil(subj_mgr) )
{
// Request the Subject with which we will communicate.
35 // This hard-coded Subject name is not necessarily a
// good thing.
tweek::Subject_var subj =
subj_mgr->getSubject("StringSubject");
40 // If the Subject Manager knows about the Subject
// named above, then we are good to go.
if ( ! CORBA::is_nil(subj) )
{
... // Shown in the next example block
45 }
}
// We did not get a Subject Manager reference back for
// some reason.
else
50 {
vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
<< "No Subject Manager chosen--exiting.\n"
<< vprDEBUG_FLUSH;
}
55 }
// The CORBA Service initialization failed.
else
{
vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
60 << "CORBA Service failed to initialize\n"
<< vprDEBUG_FLUSH;
}
}
catch (...)
65 {
vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
<< "Caught an unknown exception!\n" << vprDEBUG_FLUSH;
}
70 return 0;
} | Here, we query input from the user to get Naming Service
information. We need the host name where the Naming Service is
running, the port on which it is listening, and the version of
IIOP to use. Using
the values we get from the user, we can construct an instance
of |
| Once we have all the necessary initialization pieces, we
can construct an instance of
|
| Now we initialize the local CORBA Service. We pass in
|
| Using the CORBA Service, we choose the Subject Manager
through which all Subject requests will be handled. The work
for making this choice is offloaded to the helper function
|
| Next, we request the reference to the remote Subject. The symbolic name we use here is the same as that specified in the server application (not shown in this chapter). Note that using a hard-coded name in this way is not recommended, but we use it here for the sake of simplicity. Once we have a reference, we verify that it is not nil before trying to use it. |
We now narrow our attention to the handling of the Subject
reference that was returned by the Subject Manager. The code shown
in Example 7.8, “client.cpp: Implementation of main(),
Part II” comes from the
“...” block in Example 7.7, “client.cpp: Implementation of main(),
Part I”. At this point in the
application execution, we know that we have a non-nil
tweek::Subject reference, so we need
to narrow it to our specific type, create an Observer servant, and
attach it to the remote Subject.
Example 7.8. client.cpp: Implementation of main(),
Part II
1 StringObserverImpl* string_observer(NULL);
PortableServer::ObjectId_var observer_id;
try
5 {
// Attempt to narrow subj to the more specific reference type
// CxxClientTest::StringSubject_var. If this fails, an
// exception will be thrown and caught below.
CxxClientTest::StringSubject_var string_subj =
10 CxxClientTest::StringSubject::_narrow(subj);
// Request the current value before we create the Observer.
// In this way, we can see the persistent state maintained
// by the Subject.
15 char* cur_value = string_subj->getValue();
std::cout << "Current string value is '" << cur_value << "'"
<< std::endl;
delete cur_value;
20 // Create our Observer servant.
string_observer = new StringObserverImpl(string_subj);
// Register the newly created servant with our ORB's POA.
observer_id =
25 corba_service.registerObject(string_observer,
"StringObserver");
// This could be done in the StringObserverImpl constructor,
// but we do it here in this example just to make it clear
30 // that it is important.
string_subj->attach(string_observer->_this());
const std::string exit_string("Q");
std::string cur_string;
35
for ( ;; )
{
std::cout << "Enter a string (Q to quit): ";
std::cin >> cur_string;
40
if ( exit_string != cur_string )
{
string_subj->setValue(cur_string.c_str());
}
45 else
{
break;
}
}
50 }
catch (...)
{
vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
<< "Caught an unknown exception during object interaction!\n"
55 << vprDEBUG_FLUSH;
}
// We're done, so now we have to clean up after ourselves.
// The order of operations here is important.
60 if ( NULL != string_observer )
{
string_observer->detach();
corba_service.unregisterObject(observer_id);
delete string_observer;
65 }The last part of the client application is the choice of the
Subject Manager reference to use. In this example, we put that code
in the helper function chooseSubjectManager().
In this function, we request the list of valid Subject Manager
references from the local CORBA Service and present the information
about each one to the user. Using this information, the user selects
one, and that reference is then returned to the caller. In a
real-world example, the Subject Manager chooser would be much more
sophisticated than what we show in Example 7.9, “client.cpp: Implementation of
chooseSubjectManager()”, but for the
purposes of explaining the ideas, this will suffice.
Example 7.9. client.cpp: Implementation of
chooseSubjectManager()
1 tweek::SubjectManager_var
chooseSubjectManager(tweek::CorbaService& corbaService)
{
tweek::SubjectManager_var subj_mgr;
5
// Request all the active Subject Manager references.
std::list<tweek::SubjectManager_var> mgrs =
corbaService.getSubjectManagerList();
10 std::list<tweek::SubjectManager_var>::iterator cur_mgr;
// Iterate over all the tweek::SubjectManager references we
// have.
for ( cur_mgr = mgrs.begin();
15 cur_mgr != mgrs.end();
++cur_mgr )
{
try
{
20 // It is not entirely safe to assume that *cur_mgr is
// still valid at this point, even though it was valid
// when the mgrs list was constructed. Hence, we test
// it again now.
if ( ! (*cur_mgr)->_non_existent() )
25 {
std::string response;
const std::string proceed("y");
tweek::SubjectManager::SubjectManagerInfoList_var
30 mgr_info = (*cur_mgr)->getInfo();
std::cout << "\nSubject Manager information:"
<< std::endl;
35 // Loop over the information items and print each
// key/value pair to the screen.
for ( CORBA::ULong i = 0;
i < mgr_info->length();
++i )
40 {
std::cout << "\t" << mgr_info[i].key << " = "
<< mgr_info[i].value << std::endl;
}
45 std::cout << "Use this Subject Manager (y/n)? ";
std::cin >> response;
if ( proceed == response )
{
50 subj_mgr = *cur_mgr;
break;
}
}
}
55 catch (...)
{
vprDEBUG(vprDBG_ALL, vprDBG_CRITICAL_LVL)
<< "Caught an unknown exception in "
<< "chooseSubjectManager loop\n"
60 << vprDEBUG_FLUSH;
}
}
return subj_mgr;
65 }With that, we are done with our review of the C++ client API in Tweek. The use of CORBA allows Java and C++ client code to be quite similar, and this can be helpful when migrating between the two. The addition of the C++ client API in Tweek 0.13 also demonstrates one of the basic design philosophies of Tweek: clients can be written in any language without concern for the language the server uses.
Table of Contents
Table of Contents
This appendix provides makefiles that can be used as starting points for compiling the example code shown earlier. These can also be used as the basis for future projects using Tweek.
When compiling on Windows, special care must be taken to manage DLL symbol importing correctly. The following preprocessor symbols must be defined when compiling all user code:
USE_core_stub_in_nt_dll
USE_core_stub_in_nt_dll_NOT_DEFINED_Subject
It is especially important to define these two symbols when compiling C++ code generated by the omniORB IDL compiler (omniidl). Failure to define these symbols will result in run-time errors involving the registration of subjects with the Tweek Subject Manager. Other problems may arise as well.
The best way to define these symbols is to use the
/D option to CL.EXE so that every
C++ file compiled has the symbols defined. This can be done easily
using a Visual C++ project file or in a makefile. Example Visual C++
project files can be found with the Tweek test applications under the
directory tree
%TWEEK_BASE_DIR%\share\tweek\test.
The following is a makefile that shows how to compile the code
related to the SliderSubject example
presented in the section called “Collaborative Slider”. It runs the IDL
compiler, the C++ compiler, and the Java compiler. It assumes the use
of omniidl, the JacORB compiler, and GCC in a Linux
environment.
APP_NAME= server
all:
$(MAKE) cxx_idl
$(MAKE) java_idl
$(MAKE) NO_DEPEND=0 cxx
$(MAKE) $(APP_NAME)
$(MAKE) java
$(MAKE) NetworkTestBean.jar
-$(MAKE) install
# Basic options.
srcdir= .
NO_DEPEND= YES
IDL_CXX_FILES= SliderSubject.cpp SliderSubject.h \
WhiteboardSubject.cpp WhiteboardSubject.h
IDL_JAVA_FILES= SliderSubject.java \
WhiteboardSubject.java
base_srcs= SliderSubjectImpl.cpp \
WhiteboardSubjectImpl.cpp \
main.cpp
idl_srcs= \
$(filter-out $(base_srcs), $(notdir $(wildcard $(CXX_GEN_DIR)/*.cpp)))
SRCS= $(base_srcs) $(idl_srcs)
JAVA_SRCS= networktest/NetworkTest.java \
networktest/SliderObserverImpl.java \
networktest/SliderSubjectHolder.java \
networktest/WhiteboardObserverImpl.java \
networktest/WhiteboardSubjectHolder.java
ALL_CLASSES= networktest/*.class
include $(TWEEK_BASE_DIR)/share/tweek/tweek.appdefs.mk
CXX_GEN_DIR= .
CXX_IDL= $(TWEEK_IDL_CXX)
CXX_IDL_OPTS= $(TWEEK_IDLFLAGS_CXX) \
$(TWEEK_IDL_GENDIR_CXX)$(CXX_GEN_DIR)
CXX_IDL_INCLUDES=
EXTRA_INCLUDES+= -I$(CXX_GEN_DIR)
JAVA_GEN_DIR= $(srcdir)
JAVA_IDL= $(TWEEK_IDL_JAVA)
JAVA_IDL_OPTS= $(TWEEK_IDLFLAGS_JAVA) \
$(TWEEK_IDL_GENDIR_JAVA)$(JAVA_GEN_DIR)
JAVA_IDL_INCLUDES=
vpath %.idl $(srcdir)
vpath %.cpp $(CXX_GEN_DIR)
vpath %.h $(CXX_GEN_DIR)
vpath %.java $(JAVA_GEN_DIR)
# --------------------------------------------------------------
# Application build targets.
# --------------------------------------------------------------
cxx_idl: $(IDL_CXX_FILES)
%.cpp %.h: %.idl
$(CXX_IDL) $(CXX_IDL_OPTS) $(CXX_IDL_INCLUDES) $<
java_idl: $(IDL_JAVA_FILES)
%.java: %.idl
$(JAVA_IDL) $(JAVA_IDL_OPTS) $(JAVA_IDL_INCLUDES) $<
cxx: $(OBJS)
java: $(CLASSES)
server: $(OBJS)
$(LINK) $(LINK_OUT)$@ $(OBJS) $(TWEEK_LIBS) \
$(EXTRA_LIBS) $(LIBS)
NetworkTestBean.jar: $(JAVA_CLASSES)
$(JAR) cvf $@ $(ALL_CLASSES)
install:
cp NetworkTestBean.jar $(TWEEK_BASE_DIR)/share/tweek/beans
cp $(srcdir)/NetworkTestBean.xml \
$(TWEEK_BASE_DIR)/share/tweek/beans
CLEAN_FILES+= \
$(addprefix $(srcdir)/, $(IDL_CXX_FILES)) \
$(addprefix $(srcdir)/networktest/, $(IDL_JAVA_FILES)) \
$(CLASSES) tweek/*.class
CLEAN_DIRS+= $(srcdir)/tweek
CLOBBER_FILES+= NetworkTestBean.jarThe following is a makefile that shows how to compile the code related to the file loader Bean example presented in the section called “File Loader”. All it must do is compile the Java code for the Bean.
all:
$(MAKE) java
$(MAKE) FileOpenTestBean.jar
-$(MAKE) install
# Basic options.
srcdir= .
NO_DEPEND= YES
JAVA_SRCS= fileopentestbean/FileOpenTestBean.java
ALL_CLASSES= fileopentestbean/*.class
include $(TWEEK_BASE_DIR)/share/tweek/tweek.appdefs.mk
# --------------------------------------------------------------
# Application build targets.
# --------------------------------------------------------------
java: $(CLASSES)
FileOpenTestBean.jar: $(JAVA_CLASSES)
$(JAR) cvf $@ $(ALL_CLASSES)
install:
cp FileOpenTestBean.jar $(TWEEK_BASE_DIR)/share/tweek/beans
cp $(srcdir)/FileOpenTestBean.xml \
$(TWEEK_BASE_DIR)/share/tweek/beans
CLOBBER_FILES+= FileOpenTestBean.jarIn order to use CORBA, a CORBA implementation must be available. As of this writing, Tweek uses omniORB 3.0.4 and newer [Omn02] for a C++ ORB. Since Tweek uses the POA, any C++ ORB may be used with only a few changes to the code.
For the Java side, OpenORB 1.2.0 [Ope02] and newer [Ope03] have been used. Java IDL [Jid02], the CORBA implementation that comes with the Java Development Kit (JDK), does not fully implement the POA as of JDK 1.3.1. With the release of JDK 1.4.0, Java IDL has a complete, working POA implementation and can be used without taking any special steps.
To use an alternate ORB with Java, two arguments must be passed to the Java virtual machine. They are based on the ORB implementation. For example, if using OpenORB, the following two arguments must be specified:
-Dorg.omg.CORBA.ORBClass=org.openorb.CORBA.ORB
-Dorg.omg.CORBA.ORBSingletonClass=org.openorb.CORBA.ORBSingleton
Other ORBs will vary in the location of the
ORBClass and the
ORBSingletonClass. Refer to the documentation of
the specific ORB to find out more about using it instead of Java
IDL.
For Python, most testing thus far has been done with omniORBpy 2.0 and newer. Using ominORBpy requires that omniORB be installed since omniORBpy is primarily just a Python wrapper wround the C++ CORBA implementation. We are watching the development of Fnorb [Fno03] as an alternative to omniORBpy. Fnorb offers a pure-Python implementation of CORBA. In other words, it requires no natively compiled code to operate. Prior to December 2003, Fnorb lacked a POA implementation, so it was not a viable option, but work is still under way to provide a complete CORBA 2.3 implementation.
A full binary distribution of Tweek comes with binary code from several other software projects not affiliated with the Juggler Project or Iowa State University. Tweek makes use of redistributable code from the following packages:
JacORB (http://www.jacorb.org/)
JDOM (http://www.jdom.org/)
JGoodies look and feel (http://www.jgoodies.com/)
Kunststoff look and feel (http://www.incors.org/)
Liquid look and feel (http://liquidlnf.sourceforge.net/)
Metouia look and feel (http://mlf.sourceforge.net/)
OpenORB (http://openorb.sourceforge.net/)
The licenses for each of these software packages can be found in
$TWEEK_BASE_DIR/share/tweek/data/licenses.
Table of Contents
FSF Copyright note
Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.
This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.
This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.
A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.
The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.
A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.
You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display copies.
If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.
You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:
GNU FDL Modification Conditions
If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.
You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".
You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.
A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.
Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.
You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.
Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.
To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:
Sample Invariant Sections list
Copyright (c) YEAR YOUR NAME. 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 no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this:
Sample Invariant Sections list
with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation.
If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.
[Fno03] Fnorb website.
[Gam95] Design Patterns. Copyright © 1995 Addison Wesley Longman, Inc.. 0-201-63361-2. Addison-Wesley.
[Hen99] Advanced CORBA Programming with C++. Copyright © 1999 Addison Wesley Longman, Inc.. 0-201-37927-9. Addison Wesley Longman, Inc..
[Jbe02] JavaBeans website.
[Jid02] Java IDL website.
[Omn02] omniORB website.
[Ope02] OpenORB website.
[Ope03] OpenORB community website.
[Pyq03] PyQt website.
The Common Object Request Broker Architecture (CORBA) is a standard specified by the Object Management Group for distributed object-oriented programming. It is both platform- and language-independent. Implementations of CORBA are available from many vendors for a wide variety of programming languages.
Distributed programming is a paradigm in which software components are installed on physically separated computers and accessed remotely though a network. Examples of distributed programming include Sun's Remote Procedure Calls (RPC), Java's Remote Method Invocation (RMI), Microsoft's Component Object Model (COM), and the Common Object Request Broker Architecture (CORBA). RPC is based on the procedural programming paradigm used by languages such as C and Pascal. With RPC, procedures residing in memory on remote machines are accessed. The other examples listed are based on the object-oriented programming paradigm used by languages such as C++, Java, Smalltalk, and Python. In this case, software objects and their methods are accessed remotely.
Graphical user interfaces are used in modern operating systems that support the concept of “windows”. The windows provide a visual representation of an application and usually have a border and a title to distinguish one window from another. Within the window, there are graphical elements known as “widgets” that make up the full interface. Widgets include pull-down menus, clickable buttons, and scroll bars.
A software tool that reads an IDL file and generates code in a specific language (e.g., Java, C++, Smalltalk, etc.). This code may be anything, but typically, it is stub or skeleton code that is extended by user-defined code that completes the interface implementation.
Several types of “interfaces” exist within the scope of Tweek:
In Java. there is a keyword interface
that can be used to define a set of methods that must be part of
an implementing class. Classes that claim to implement a given
interface must define the methods described by the interface or
declare themselves abstract.
In IDL, an interface is essentially identical to a Java interface, but an IDL compiler can generate code from the IDL. The interface methods must still be implemented (by a C++ class, for example), and thus, the main function of an IDL interface is to define a language-independent signature through which CORBA communication may occur.
In C++, there is no interface keyword
as in Java and IDL. The term “interface” is
typically applied to abstract classes (those with pure virtual
methods). More generally, it is use to refer to the collection
of methods defined by a class.
The Interface Definition Language is a simple language used to define the interface (or signature) an object will have. An interface is composed solely of methods (functions) that may be invoked on an object (data element). No data members are present in the interface definition. IDL is not tied to a specific language but instead must be compiled into another language where the interface will be implemented.
The Internet Inter-ORB Protocol (IIOP) is part of the CORBA specification. It is used to standardize communication between ORBs so that ORBs from different vendors can inter-operate seamlessly.
JavaBeans are the component architecture of the Java
programming language. There are no restrictions on the interfaces
implemented by JavaBeans, though it is strongly recommended that
they implement
java.io.Serializable.
The Java Native Interface is the bridge between a Java virtual machine (JVM) and natively compiled code. Native code can load a JVM and get access to Java objects through it, or Java classes may have native methods that are loaded by the JVM.
An Object Request Broker (ORB) is used by CORBA for management of interface implementation objects (servants) and the references made to those objects. ORBs communicate with each other using a client/server model, though two ORBs may both act as servers and clients during the transactions.
The Portable Object Adapter (POA) the standard object adapter and is part of the CORBA 2.3 specification. It is used together with IIOP to allow ORB implementations from different vendors to communicate. A single ORB may have multiple POAs each tailored to a certain task using parameterized characteristics.
Remote Method Invocation (RMI) is a mechanism for gaining access to remote Java code. It is implemented as part of the Java virtual machine and was originally intended to be comparable to CORBA. RMI allows easy access to code between two virtual machines, but it does not have the cross-language features of CORBA.