These section describes how to code a CORBA C++ client that invokes component methods:
Stub header files are generated for all IDL modules that include interfaces that the component implements—you must include all these stub header files. In addition to the stub header files, you must also include SessionManager.hpp (which contains the classes and functions that allow a C++ client to create and destroy sessions) in the client source file.
You can also include these optional header files:
TabularResults.hpp – contains the classes and functions that allow C++ clients to receive result sets from components.
CosNaming.hpp – contains the classes and functions that allow C++ clients to use the EAServer’s name service feature to bind a component to a name that must be unique within a naming context.
BCD.hpp – contains the mappings for binary and arbitrary precision floating point-decimal datatypes.
MJD.hpp – contains the datatype mappings from CORBA to C++ for Modified Astronomical Julian Date (M.J.D.) dates and times.
TabularResults.hpp already includes BCD.hpp and MJD.hpp;
if you include TabularResults.hpp, you do not
have to include BCD.hpp and MJD.hpp.
You must use scoped names to the CORBA IDL module, the EAServer SessionManager IDL module, and any component IDL modules that you want to execute methods on. To make using scoped names easier, you can use the C++ using statement for the IDL module namespaces as in the following example:
using namespace CORBA; using namespace SessionManager;
If your C++ compiler does not support namespaces, define the compiler macro JAG_NO_NAMESPACE when compiling your source files.
When you create an object, identify the object reference by appending _var to the object name. The ObjectName_var reference will be automatically released when it is deallocated or assigned a new object reference.
CORBA::is_nil(Object) can be used to verify that a specific interface is implemented by a component. For an example, see “Creating a Manager instance”.
If you are returning result sets from components, you should also specify the TabularResults EAServer IDL module with the using statement.
Before invoking methods on component instances, the client must connect to a server and instantiate the components. Your code must perform these steps to create proxy instances:
Step |
What it does |
Detailed explanation |
---|---|---|
1 |
Initialize the CORBA ORB and create an ORB reference. |
|
2 |
Use the ORB reference to create a Manager instance. |
|
3 |
Use the Manager instance to create a Session. |
|
4 |
Use the Session instance to create stub component instances. |
|
5 |
Call the stub methods to remotely invoke component methods. |
Except for the example in “Processing result sets”, the same client source code
is used as an example throughout this section. Only the parts relevant
to each step are used.
Before you can use any ORB classes, you must call the ORB_init method, which:
Returns an object reference to the ORB.
Allows you to pass initialization parameters to the driver class in the form of a string array. You can also set an environment variable (in the System Properties for your machine) for each initialization parameter. If the environment variable and initialization parameter are set, the value of the initialization parameter is used. You can set any initialization parameter to a value of none, which overrides the value of the environment variable and sets the value to the default, if any.
You can pass the following initialization parameters to the driver class:
ORBHttp – this specifies whether the ORB should use HTTP-tunnelling to connect to the server. A setting of of "true" specifies HTTP tunnelling. The default is "false". This parameter can also be set in an environment variable, JAG_HTTP. Some firewalls may not allow IIOP packets through, but most all allow HTTP packets through. When connecting through such firewalls, set this property to "true".
ORBHttpExtraHeader – An optional setting to specify what extra information is appended to the header of each HTTP packet when connecting through a Web proxy. See Chapter 12, “Deploying Applications Around Proxies and Firewalls,” in the EAServer Security Administration and Programming Guide for more information.
ORBHttpUsePost – when using HTTP tunnelling, specifies the HTTP request type used. A value of true indicates that POST requests are to be used. A value of false (the default) specifies that GET requests are to be used. This parameter can also be set in an environment variable, JAG_HTTPUSEPOST.
ORBLogIIOP – this specifies whether the ORB should log IIOP protocol trace information. A setting of "true" enables logging. The default is "false". This parameter can also be set in an environment variable, JAG_LOGIIOP. When this parameter is enabled, you must set the ORBLogFile option (or the corresponding environment variable) to specify the file where protocol log information is written.
ORBLogFile – this sets the path and name of the file to which to log client execution status and error messages. This parameter can also be set in an environment variable, JAG_LOGFILE. The default setting is no log.
ORBCodeSet – this sets the code set that the client uses. This parameter can also be set in an environment variable, JAG_CODESET. The default setting is iso_1.
ORBRetryCount – specify the number of times to retry when the initial attempt to connect to the server fails. This parameter can also be set in an environment variable, JAG_RETRYCOUNT. The default is 5.
ORBRetryDelay – specify the delay, in milliseconds, between retry attempts when the initial attempt to connect to the server fails.This parameter can also be set in an environment variable, JAG_RETRYDELAY. The default is 2000.
ORBProxyHost – specifies the machine name or the IP address of an reverse proxy server. See Chapter 12, “Deploying Applications Around Proxies and Firewalls,” in the EAServer Security Administration and Programming Guide for more information.
ORBProxyPort – specifies the port number of a reverse proxy server.
ORBforceSSL – force an SSL connection to a reverse proxy server (indicated by the ORBProxyHost and ORBProxyPort properties). Set this property to true if the connection to the reverse proxy must use SSL (HTTPS) tunnelling, but the connection from the proxy to the server does not use SSL tunnelling.
ORBsocketReuseLimit – specifies the number of times that a network connection may be reused to call methods from one server. The default is 0, which indicates no limit. The default is ideal for short-lived clients. The default may not be appropriate for a long-running client program that calls many methods from servers in a cluster. If sockets are reused indefinitely, the client may build an affinity for servers that it has already connected to rather than randomly distributing its server-side processing load among all the servers in the cluster. In these cases, the property should be tuned to best balance client performance against cluster load distribution. In Sybase testing, a setting of 10 to 30 proved to be a good starting point. If the reuse limit is too low, client performance degrades.
ORBIdleConnectionTimeout – specifies the time, in seconds, that a connection is allowed to sit idle. When the timeout expires, the ORB closes the connection. The default is 0, which specifies that connections can never timeout. The connection timeout does not affect the life of proxy instance references; the ORB may close and reopen connections transparently between proxy method calls. Specifying a finite timeout for your client applications can improve server performance. If many instances of the client run simultaneously, a finite client connection timeout limits the number of server connections that are devoted to idle clients. A finite timeout also allows rebalancing of server load in an application that uses a cluster of servers.
ORBWebProxyHost – the host name or IP address of an HTTP proxy server that supports generic Web tunnelling, sometimes called connect-based tunnelling. There is no default for this property, and you must specify both the host name and port number properties. See Chapter 12, “Deploying Applications Around Proxies and Firewalls,” in the EAServer Security Administration and Programming Guide for more information. You can also specify the property by setting the environment variable JAG_WEBPROXYHOST.
ORBWebProxyPort – when
generic Web tunnelling is enabled by setting ORBWebProxyHost
,
this property specifies the port number at which the HTTP proxy
server accepts connections. There is no default for this property,
and you must specify both a host name and port. See Chapter
12, “Deploying Applications Around Proxies and Firewalls,” in
the EAServer Security Administration and Programming Guide for
more information. You can also specify the property by setting the
environment variable JAG_WEBPROXYPORT.
ORBHttpExtraHeader – an
optional setting to specify what extra information is appended to
the header of each HTTP packet sent to a proxy server (specified
with the ORBWebProxyHost
parameter).
You can also specify the property by setting the property JAG_HTTPEXTRAHEADER. See Chapter
12, “Deploying Applications Around Proxies and Firewalls,” in
the EAServer Security Administration and Programming Guide for
more information.
You can pass additional properties to configure secure (IIOPS) connections. See Chapter 6, “Using SSL in C++ Clients,” in the EAServer Security Administration and Programming guide for more information.
ORB initialization is demonstrated in this example. You can specify the ORB options as a command line parameters to be passed to the ORB_init method.
#include <stdio.h> #include <iostream.h> #include <string.h> #include <SessionManager.hpp> #include <CosNaming.hpp> #include <Jaguar.hpp> #include <Tutorial.hpp> // Stubs for interfaces in Tutorial IDL // module. int main(int argc, char** argv) { const char *usage = "Usage:\n\tarith -ORBNameServiceURL iiop:// <host>:<iiop-port>/<initial-context>\n"; const char *tutorial_help = "Check EAServer Manager and verify that the" "Tutorial/CPPArithmetic component exists " "and that it implements the " "Tutorial::CPPArithmetic IDL interface."; const char *ior_prefix = "iiop://"; const char *component_name = "Tutorial/CPPArithmetic"; char *ior = NULL; try { cout << "Creating Jaguar session\n\n"; // Initialize the ORB CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, 0);
The SessionManager::Manager interface is used for client authentication for EAServer connections. To create a Manager instance, you must identify the server by using:
The Interoperable Object Reference (IOR) for the server, or
The URL for the server.
The IOR string encodes the server’s host address and the port at which the server accepts IIOP requests. Each time EAServer is started, for each listener the server prints a hex-encoded IOR string with standard encoding to the following files in the EAServer html subdirectory:
<listener><iiop-version>.ior – Contains the IOR string by itself.
<listener>_<iiop-version>_param.ior – Contains the IOR as part of an HTML PARAM definition that can be inserted into an APPLET tag.
<listener> is the name of the listener.
<iiop-version> is the version of IIOP and can be either 10, which represents IIOP version 1.0, or 11, which represents IIOP version 1.1.
For example, a server will generate the following files for a listener, iiops2:
iiops2_10.ior
iiops2_11.ior
iiops2_10_param.ior
iiops2_11_param.ior
You can code your C++ client to retrieve the IOR string from one of the <listener><iiop-version>.ior files.
The server’s IIOP port is configured in EAServer Manager using listeners. In the default configuration, the IIOP port number is 9000.
Once the client has obtained the server’s IOR or URL string, it calls the ORB::string_to_object method to convert the IOR or URL string into a Manager instance, as shown in the following example. You use the Manager::_narrow method to return a new object reference for the existing object, which is the IOR object.
... Object_var object = orb->string_to_object ("iiop://myhost:9000"); Manager_var manager = Manager::_narrow (object); if (is_nil(manager)) { cout << "Error: Null SessionManager::Manager instance. Exiting."; return -1; }...
string_to_object returns
an object reference to the URL, iiop://jagpc3:9000
,
as object. For each reference, the _var form
is used because the object will be automatically released when it
is deallocated or assigned a new object reference. _narrow converts object into
object reference for Manager.
_narrow returns a nil object reference if the component does not implement the interface. is_nil(manager) verifies that the SessionManager::Manager interface is implemented and returns an error if the interface is not implemented.
The SessionManager::Session interface represents an authenticated session between the client application and a server. The Manager::createSession method accepts a user name and password and returns a Session_var object, session, as shown in the example below:
... Session_var session = manager.createSession("jagadmin", ""); ...
You call the Session::lookup method to return a factory for proxy object references. The signature of Session::lookup is:
SessionManager::Factory_var lookup("name")
Session::lookup takes a string that specifies
the name of the component to instantiate. A component’s
default name is the EAServer package name and the component name,
separated by a slash as in calculator/calc.
However, a different name can be specified with the component’s com.sybase.jaguar.component.naming
property.
For example, you can specify a logical name, such as USA/MyCompany/FinanceServer/Payroll.
For more information on configuring the naming service, see Chapter
5, “Naming Services,” in the EAServer
System Administration Guide.
Session::lookup returns a factory for component proxies. Call the Factory::create method to obtain proxies for the component. This method returns a org.omg.CORBA.Object reference. Call _narrow to convert the object reference into an instance of the stub class for the component.
The code to call Session::factory and Factory::create looks like this:
... // In this example, the component is named // Repository and is installed in // the EAServer package. Object_var obj = session->lookup("Jaguar/Repository"); SessionManager::Factory_var repoFactory = SessionManager::Factory::_narrow(obj); obj = repoFactory->create(); Jaguar::Repository_var repository = Jaguar::Repository::_narrow(obj); // Verify that we really have an instance. if (CORBA::is_nil(repository)) { cout << "ERROR: Null instance for component."; }
Calling Session.lookup in server code
When called from server code, Session::lookup resolves
the component name by calling the name service, which gives preference
to a local component instance if the component is installed on the
same server. However, the use of a locally installed component is
not guaranteed. To ensure that a local implementation is used, specify
the name as
local:package/component
, where package is
the package name and component is the component
name, for example, local:CtsSecurity/SessionInfo
.
When you specify the local: prefix, the lookup call
bypasses the name service and returns a local instance if the component
is installed in the same server. The call fails if the specified component
is not installed in the same server..
After instantiating the stub class, use the stub class instance to invoke the component’s methods. The stub class has methods that correspond to each method in the component. Parameter datatypes are mapped as described in Table 13-1. Any parameter datatype can be used as a return type; in addition, user-defined IDL datatypes can be used as return, in, inout, or out parameters.
You can overload methods in C++ and Java, but not in ActiveX components. See “Operation declarations” and “Supported datatypes”.
In addition to the tasks described in this section, you can also explicitly manage OTS transactions from your client. See “Managing explicit OTS transactions” for more information.
To retrieve and process a single result set from a component:
Call the component method on the stub instance that returns a result set.
Iterate through each row and then each column in a row by using nested for loops.
Use the discriminator method (_d) to retrieve the datatype of the column in a row and switch/case syntax to process the column values (such as printing the column values).
To retrieve and process multiple result sets returned from a component method as a TabularResults::ResultSets object:
Call the component method on the component reference that returns the result sets.
Retrieve the length or number of result sets.
Iterate through the result sets using a for loop.
For each result set, iterate through each row and then each column in a row by using nested for loops.
You can treat a ResultSets object as an array of ResultSet objects. On each iteration, retrieve a reference to each ResultSet object by using the subscript [ ] operator.
Use the discriminator method (_d) to retrieve the datatype of the column in a row and switch/case syntax to process the column values (such as printing the column values).
This example retrieves a single result set. The following code shows the C++ client in its entirety. For detailed explanations, see the sections that explain each result-set processing step.
All of the required header files are included. The IDL module namespaces are specified with the C++ using statement. The printResultSet() method contains the logic for processing a result set. main() contains the logic to initialize and connect to the EAServer ORB, instantiate the stub, call the component method to retrieve the result set object, and call printResultSet() to process the result set.
After the result set has been processed, execution of printResultSet() ends and control is returned to main(). In main(), the screen is kept open with the fprintf statement. Once you press Return, execution ends.
#include <stdio.h> #include <time.h> #include <iostream.h> #include <SessionManager.hpp> #include <TabularResults.hpp> #include <Test.hpp>
using namespace CORBA; using namespace SessionManager; using namespace TabularResults; using namespace Test;
void printResultSet(const ResultSet& rs) { ULong nc = rs.columns.length(); cout << rs.rows << " rows, " << nc << " columns" << endl; for (ULong row = 0; row < rs.rows; row++) { cout << "row " << row << ": "; for (ULong column = 0; column < nc; column++) { if (column > 0) { cout << ", "; } BooleanSeq& nulls = ((ColumnSeq&)rs.columns)[column].nulls; if (row + 1 <= nulls.length() && nulls[row]) { cout << "null"; continue; } Data& values = ((ColumnSeq&)rs.columns)[column].values; switch (values._d()) { case TYPE_BIT: { BooleanSeq& booleanValues = values.booleanValues(); cout << (booleanValues[row] ? "true" : "false"); break; } case TYPE_TINYINT: { OctetSeq octetValues = values.octetValues(); cout << octetValues[row]; break; } case TYPE_SMALLINT: { ShortSeq& shortValues = values.shortValues(); cout << shortValues[row]; break; } case TYPE_INTEGER: { LongSeq& longValues = values.longValues(); cout << longValues[row]; break; } case TYPE_REAL: { FloatSeq& floatValues = values.floatValues(); cout << floatValues[row]; break; } case TYPE_DOUBLE: case TYPE_FLOAT: { DoubleSeq& doubleValues = values.doubleValues(); cout << doubleValues[row]; break; } case TYPE_CHAR: case TYPE_LONGVARCHAR: case TYPE_VARCHAR: { StringSeq& stringValues = values.stringValues(); cout << stringValues[row]; break; } case TYPE_BINARY: case TYPE_LONGVARBINARY: case TYPE_VARBINARY: { BinarySeq& binaryValues = values.binaryValues(); cout << "(binary)"; break; } case TYPE_BIGINT: case TYPE_DECIMAL: case TYPE_NUMERIC: { DecimalSeq& decimalValues = values.decimalValues(); cout << "(decimal)"; break; } case TYPE_DATE: { DateSeq& dateValues = values.dateValues(); // Assumption: time_t is seconds from Jan 1, 1970 time_t t = (time_t)((dateValues[row].dateValue - 40222.0) * 86400); cout << ctime(&t); break; } case TYPE_TIME: { TimeSeq& timeValues = values.timeValues(); cout << "time: " << timeValues[row].timeValue; break; } case TYPE_TIMESTAMP: { TimestampSeq& timestampValues = values.timestampValues(); time_t t = (time_t)((timestampValues[row].dateValue + timestampValues[row].timeValue - 40222.0) * 86400); cout << ctime(&t); break; } } } cout << endl; } } int main(int argc, char** argv) { ORB_var orb = ORB_init(argc, argv, ""); Manager_var manager = Manager:: _narrow(Object_var(orb->string_to_object("iiop://myhost:9000"))); Session_var session = manager->createSession("jagadmin", ""); Ping_var p = Ping::_narrow(Object_var(session->create("Test/Java"))); ResultSet_var rs = p->results(); printResultSet(rs.in()); { char c; fprintf(stderr, "Press Return to continue..."); c = getchar(); } return 0; }
To retrieve the result set, you must instantiate the stub and call the component method that returns a result set to the client. This example instantiates the stub from the Java component in the Test package in a session as an object p of type Ping_var using the _narrow method. The component method, results() is called on p which returns the result set rs.
Ping_var p = Ping::_narrow(Object_var(session->create("Test/Java"))); ResultSet_var rs = p->results();
You must process each column value of each row one at a time. In this example, the processing is contained in a method (which you can reuse in other applications) called printResultSet(). printResultSet() takes the result set rs as an input parameter.
printResultSet(rs.in());
The method uses the length() method to determine how many columns, nc, are in the result set, rs, and displays the number of columns and rows; the number of rows is represented by the variable rows. The method uses a for loop to iterate through each row, row, in the result set; and a nested for loop to iterate through each column, column, in the current row. The method must check for null values before it can process and print the values in each of the columns of the current row. After checking for and printing out null values, the method continues to the next column in the current row.
void printResultSet(const ResultSet& rs) { ULong nc = rs.columns.length(); cout << rs.rows << " rows, " << nc << " columns" << endl; for (ULong row = 0; row < rs.rows; row++) { cout << "row " << row << ": "; for (ULong column = 0; column < nc; column++) { if (column > 0) { cout << ", "; } BooleanSeq& nulls = ((ColumnSeq&)rs.columns)[column].nulls; if (row + 1 <= nulls.length() && nulls[row]) { cout << "null"; continue; }
In the body of printResultSet(), the _d() method (the discriminator method) is used to retrieve the datatype of the column and switch/case processing is used to process the column value in the current row. values is a reference to a Data object that represents the column value. _d() returns the datatype of the referenced value to the switch statement and the body of the case statement that matches the datatype is executed. In each case, the current row’s column value that corresponds to the case’s datatype is printed.
For the Date, Time, Timestamp datatypes, some conversion is required to print a value in a standard format (such as “January 5, 1998”).
Data& values = ((ColumnSeq&)rs.columns)[column].values; switch (values._d()) { case TYPE_BIT: { BooleanSeq& booleanValues = values.booleanValues(); cout << (booleanValues[row] ? "true" : "false"); break; } case TYPE_TINYINT: { OctetSeq octetValues = values.octetValues(); cout << octetValues[row]; break; } case TYPE_SMALLINT: { ShortSeq& shortValues = values.shortValues(); cout << shortValues[row]; break; } case TYPE_INTEGER: { LongSeq& longValues = values.longValues(); cout << longValues[row]; break; } case TYPE_REAL: { FloatSeq& floatValues = values.floatValues(); cout << floatValues[row]; break; } case TYPE_DOUBLE: case TYPE_FLOAT: { DoubleSeq& doubleValues = values.doubleValues(); cout << doubleValues[row]; break; } case TYPE_CHAR: case TYPE_LONGVARCHAR: case TYPE_VARCHAR: { StringSeq& stringValues = values.stringValues(); cout << stringValues[row]; break; } case TYPE_BINARY: case TYPE_LONGVARBINARY: case TYPE_VARBINARY: { BinarySeq& binaryValues = values.binaryValues(); cout << "(binary)"; break; } case TYPE_BIGINT: case TYPE_DECIMAL: case TYPE_NUMERIC: { DecimalSeq& decimalValues = values.decimalValues(); cout << "(decimal)"; break; } case TYPE_DATE: { DateSeq& dateValues = values.dateValues(); // Assumption: time_t is seconds from Jan 1, 1970 time_t t = (time_t)((dateValues[row].dateValue - 40222.0) * 86400); cout << ctime(&t); break; } case TYPE_TIME: { TimeSeq& timeValues = values.timeValues(); cout << "time: " << timeValues[row].timeValue; break; } case TYPE_TIMESTAMP: { TimestampSeq& timestampValues = values.timestampValues(); time_t t = (time_t)((timestampValues[row].dateValue + timestampValues[row].timeValue - 40222.0) * 86400); cout << ctime(&t); break; } } } cout << endl; } }
The client-side ORB throws two kinds of exceptions:
CORBA system exceptions – These exceptions are defined in the CORBA specification.
User-defined exceptions – These exceptions must be defined in the component’s IDL definition.
The CORBA specification defines the list of standard system exceptions. In C++, all CORBA system exceptions are mapped to a C++ class that is derived from the standard SystemException class defined in the CORBA module. You may want to trap the exceptions shown in this code fragment:
try { ... // invoke methods } catch (CORBA::COMM_FAILURE& cf) { ... // A component aborted the EAServer transaction, // or the transaction timed out. Retry the // transaction if desired. } catch (CORBA::TRANSACTION_ROLLEDBACK& tr) { ... // possibly retry the transaction } catch (CORBA::OBJECT_NOT_EXIST& one) { ... // Received when trying to instantiate // a component that does not exist. Also // received when invoking a method if the // object reference has expired // (this can happen if the component // is stateful and is configured with // a finite Instance Timeout property). // Create a new proxy instance if desired.} } catch (CORBA::NO_PERMISSSION& np) { ... // tell the user they are not authorized } catch (CORBA::SystemException& se) { ... // report the error but don’t bother retrying }
Not all of the possible system exceptions are shown
in the example. See the CORBA/IIOP 2.2 Specification (formal/98-02-01)
for a list of all the possible exceptions.
In C++, all CORBA user-defined exceptions are mapped to a C++ class that is derived from the standard UserException class defined in the CORBA module. For more information, see “User-defined IDL datatypes” and “User-defined exceptions”.
User-defined types must exist in the EAServer IDL repository
before you can use them in interface declarations.
Copyright © 2005. Sybase Inc. All rights reserved. |
![]() |