Previous Up Next

Chapter 25  Portability of CORBA Applications

Most of the source-code of a CORBA application is portable across different CORBA products. In fact, portability is so good that it is easier to discuss the few areas in which portability is lacking. The 80/20 principle [Koc00] applies to porting: most of the difficultly of porting an application is rooted in a relatively small number of issues. By being forewarned about these issues, a development team can pro-actively work to avoid common pitfalls and so make porting easier and cheaper. This chapter focuses on C++ and Java because those are the languages that the author has used for CORBA development.

25.1  CORBA Portability Issues

CORBA has standardized the mapping from IDL to many different programming languages, such as C++, Java, Cobol, Ada and so on. This means that the way a programmer manipulates a struct or union, makes a remote call using a proxy or implements an interface with a servant class is exactly the same across all C++ CORBA products, is exactly the same across all Java CORBA products, and so on. However, there are still a few CORBA-related portability issues to watch out for, as the following subsections discuss.

25.1.1  Makefile Issues

CORBA has not standardized the name of the IDL compiler or the command-line options that it takes. Neither has CORBA standardized the names of libraries with which CORBA programs must be linked. A practical ramification is that a Makefile (or equivalent, such as a Microsoft Visual Studio Project file) will need to be modified if you switch from using one CORBA product to another. Making such modifications is usually a straightforward, albeit somewhat tedious task.

25.1.2  Names of CORBA-related C++ Header Files

The IDL-to-C++ mapping defines the APIs of generated data-types. Unfortunately, the mapping does not standardize the names of the generated source-code files in which the data-types reside. For example, given the file foo.idl, the Orbix/C++ IDL compiler generates foo.hh, fooS.hh, fooC.cxx and fooS.cxx. IDL compilers provided with other CORBA products are likely to use different suffixes on the names of generated files.

Changing the names of source-code files listed in a Makefile is usually not a big problem. What can be a much bigger problem is changing numerous #include directives in source-code files, because most source-code files in a project may #include one or more CORBA-related header files. The Portability of C++ CORBA Applications chapter of the CORBA Utilities package [McH] discusses this issue in depth and explains how investing a few hours of up-front work on this issue at the start of a project can produce portable #include directives that can save days or even weeks of frustration later on when porting an application.

25.1.3  Configuration and Logging APIs

CORBA does not provide standardized APIs for: (1) obtaining runtime configuration values, or (2) directing diagnostic messages to a log file. However, most CORBA products provide proprietary APIs for these purposes. This is because the internals of CORBA products require such capabilities, and CORBA vendors often decide to expose these internally-required capabilities to application developers. If you avoid use of these proprietary APIs then porting to a different CORBA product will be much easier. If you must make use of such proprietary APIs then you should not access them directly, but rather write a thin “portability wrapper” API around them. In this way, you minimize the amount of code that must be modified when porting an application.

25.1.4  Implementation Repositories

As Chapter 7 discusses, CORBA has not standardized upon the “look and feel” of implementation repositories (IMRs) and, for this reason, there is considerable difference between the IMRs of different CORBA products. Put simply, there is no portability in the administration of CORBA applications across different CORBA products.

Some server deployment models (Section 8.2.2) require use of proprietary APIs in some CORBA products. Direct use of these proprietary APIs can obviously hinder portability. The Creation of POA Hierarchies Made Simple chapter of the CORBA Utilities package [McH] discusses a Java and C++ class that provides a “portability wrapper” around such proprietary APIs, while simultaneously simplifying application development.

25.1.5  Multi-threaded Servers

Section 6.1.2.1 explained that the ORB_CTRL_MODEL POA policy has under-defined semantics. Most CORBA products either implement a thread pool for this policy or offer a thread pool as one of a configurable set of alternatives for this policy. For this reason, it is reasonably portable to assume that this POA policy is implemented as a thread pool, where the size of the thread pool is determined through an entry in a configuration file, and that the thread pool is created automatically by the CORBA runtime system. However, one notable exception to this is TAO, which is a C++ freeware implementation of CORBA. TAO does support thread pool semantics for the ORB_CTRL_MODEL POA policy. However, TAO requires that the application programmer create all the threads in the thread pool, and each of these threads must call orb->run(), which is how the application-level threads become part of the thread pool.


int main(int argc, char* argv[]) { thread_pool_size = ...; exit_status = 0; orb = CORBA::ORB::_nil(); try { orb = CORBA::ORB_init(argc, argv); ... // create POAs to contain servants ... // create servants and activate into POAs ... // export object references ... // activate POA managers 1 create_tao_thread_pool(orb, thread_pool_size - 1); 2 orb->run(); } catch(const CORBA::Exception & ex) { cout << "Something went wrong: " << ex << endl; exit_status = 1; } 3 wait_for_tao_thread_pool_to_terminate(); // Terminate gracefully try { if (!CORBA::is_nil(orb)) { orb->destroy(); } } catch(CORBA::Exception & ex) { cout << "Something went wrong: " << ex << endl; exit_status = 1; } return exit_status; };
Figure 25.1: Server mainline using portability abstraction for TAO thread pools

The pseudocode in Figure 25.1 shows how to write a portable C++ server that uses a thread pool for ORB_CTRL_MODEL POAs. The important point is to have two portability “wrapper” functions for creating and destroying a thread pool. Usage of these is shown at lines 1 and 3, respectively. All but one of the threads for the thread pool are created at line 1; the last thread in the thread pool is the main thread that calls orb->run() directly (line 2).

The implementation of the portability wrapper functions is given in Figure 25.2. The implementation uses the existence of the P_USE_TAO preprocessor symbol (a discussion of which can be found elsewhere [McH, Ch. 4]) to choose between the TAO-specific code for creating and destroying a thread pool, or dummy implementations of the functions that are appropriate for most other C++ CORBA implementations.


#ifdef P_USE_TAO #include "ace/Task.h" class Worker : public ACE_Task_Base { CORBA::ORB_var m_orb; public: Worker(CORBA::ORB_ptr orb) { m_orb = CORBA::ORB::_duplicate(orb); } virtual int svc(void) { try { m_orb->run(); } catch(...) { } return 0; } }; static Worker * w = 0; void create_tao_thread_pool(CORBA::ORB_ptr orb, int count) throw(std::string) { w = new Worker(orb); if (w->activate(THR_NEW_LWP | THR_JOINABLE, count)!=0) { delete w; w = 0; throw std::string("Cannot create thread pool"); } } void wait_for_tao_thread_pool_to_terminate() { if (w != 0) { w->thr_mgr()->wait(); delete w; w = 0; } } #else // dummy implementation for other CORBA products void create_tao_thread_pool(CORBA::ORB_ptr orb, int count) throw(std::string) { } void wait_for_tao_thread_pool_to_terminate() { } #endif
Figure 25.2: Portability wrapper for TAO thread pools

25.2  Non-CORBA Portability Issues with C++

When porting an application from one CORBA product to another, developers sometimes also switch from one compiler to another or from one operating system to another. Such switches of compiler or operating system do not affect Java-based applications very much, because Java provides a “virtual machine” that is (supposed to be) portable across all Java compilers and operating systems. Actually, when Java was first released, there were promises that it was a “write once, run everywhere” language. When developers noticed that Java-based applications did not run as fast as C or C++-based applications, some cynics said that Java was a “write once, crawl everywhere” language. Some other developers encountered subtle differences in behavior in the Java Virtual Machine (JVM) on different operating systems and so said that Java was a “write once, debug everywhere” language. However, problems with performance and JVM differences have decreased over the years and, nowadays, Java applications tend to be very portable.

Switching compilers or operating systems tends to be much more troublesome for C++-based applications than for Java-based applications. The following subsections discuss some problematic areas that you should watch out for in C++ applications.

25.2.1  Cross-platform Portability

For some reason, supposedly portable applications developed on Windows often accidentally use Windows-proprietary APIs.1 This is usually discovered only when the application is then ported to a UNIX platform. By that time, the use of Windows-proprietary APIs may be so widespread in the application that the porting becomes a major effort. For example, the Windows-proprietary CString class is often accidentally used in supposedly portable applications when the standard (and hence portable) C++ class std::string would do just as well. Some companies who have found themselves with a supposedly portable application that has been accidentally tied to CString have found it quicker to reverse engineer the CString class so they can write a UNIX version rather than remove the use of CString from their application. Some companies end up reverse engineering large parts of Microsoft’s MFC class library so that they can write a UNIX version, just to port a Windows application to UNIX.

To guard against such difficulties in the future porting of applications, it is useful if at least one developer with some UNIX experience works on the team that has the task of developing a portable application initially on Windows.

If your CORBA applications will have a graphical user interface (GUI) then you might wish to consider use of a cross-platform GUI toolkit. One such toolkit is wxWindows (www.wxwindows.org).

25.2.2  The iostream Library

Although C++ dates from the early 1980s, the language was not standardized until the mid-1990s. In pre-standardized C++, many header files had ".h" extensions, for example, <iostream.h>. The standardization committee decided to make two important changes to standard header files:

  1. The ".h" extension was dropped, for example, <iostream.h> became <iostream>.
  2. The types and variables defined in these standard header files were defined in the std namespace rather than in the global scope, for example, cout became std::cout.

In many compilers, the new standardized types are not type compatible with the older, pre-standardized types. Many projects have wasted weeks or months in porting code originally written for use with the pre-standardized types to use the standardized types. It is not uncommon for this porting headache to raise its head only when an application is being ported from one CORBA product or operating system to another. The Portability of C++ CORBA Applications chapter of the CORBA Utilities package [McH] discusses this issue in depth and explains how investing a few hours of up-front work on this issue can result in the ability to write code that compiles correctly with either the pre-standardized or the standardized C++ library. The advice in that document can dramatically reduce the time required to port applications.

25.2.3  Synchronization in C++ Applications

The C++ language does not define standard APIs for synchronization. Instead, synchronization APIs vary a lot from one operating system to another. Many UNIX platforms now support the POSIX Threads standard, but it is still possible to use proprietary APIs on some flavors of UNIX. Also, Windows uses its own proprietary APIs instead of POSIX APIs. Because of this, porting a multi-threaded application between different operating systems can involve an enormous amount of tedious work if the application was written to use the native synchronization APIs of the original platform.

Many organizations have tried to write their own “portability wrapper” around the low-level synchronization APIs of an operating system. However, doing this is fraught with difficulties. For example, it is very difficult to correctly and efficiently emulate POSIX condition variables with the synchronization APIs of Windows.

Many CORBA products work on several operating systems and it is common for a CORBA product to provide its own, proprietary “portability wrapper” for synchronization APIs. Use of these libraries provides portability across different operating systems, but makes it difficult to port an application from one CORBA product to another.

The Generic Synchronization Policies (GSP) class library [McH, Ch. 7] provides cross-platform synchronization support and is not tied to a particular CORBA product. It offers an additional benefit in that it does not try to mimic low-level APIs provided by an operating system, but rather provides a high-level API that simplifies the writing of multi-threaded programs.


1
The reverse is not true. Supposedly portable applications developed on UNIX tend to be much easier to port to Windows than supposedly portable applications developed on Windows are to port to UNIX.

Previous Up Next