- •Advanced CORBA® Programming with C++
- •Review
- •Dedication
- •Preface
- •Prerequisites
- •Scope of this Book
- •Acknowledgments
- •Chapter 1. Introduction
- •1.1 Introduction
- •1.2 Organization of the Book
- •1.3 CORBA Version
- •1.4 Typographical Conventions
- •1.5 Source Code Examples
- •1.6 Vendor Dependencies
- •1.7 Contacting the Authors
- •Part I: Introduction to CORBA
- •Chapter 2. An Overview of CORBA
- •2.1 Introduction
- •2.2 The Object Management Group
- •2.3 Concepts and Terminology
- •2.4 CORBA Features
- •2.5 Request Invocation
- •2.6 General CORBA Application Development
- •2.7 Summary
- •Chapter 3. A Minimal CORBA Application
- •3.1 Chapter Overview
- •3.2 Writing and Compiling an IDL Definition
- •3.3 Writing and Compiling a Server
- •3.4 Writing and Compiling a Client
- •3.5 Running Client and Server
- •3.6 Summary
- •Part II: Core CORBA
- •Chapter 4. The OMG Interface Definition Language
- •4.1 Chapter Overview
- •4.2 Introduction
- •4.3 Compilation
- •4.4 Source Files
- •4.5 Lexical Rules
- •4.6 Basic IDL Types
- •4.7 User-Defined Types
- •4.8 Interfaces and Operations
- •4.9 User Exceptions
- •4.10 System Exceptions
- •4.11 System Exceptions or User Exceptions?
- •4.12 Oneway Operations
- •4.13 Contexts
- •4.14 Attributes
- •4.15 Modules
- •4.16 Forward Declarations
- •4.17 Inheritance
- •4.18 Names and Scoping
- •4.19 Repository Identifiers and pragma Directives
- •4.20 Standard Include Files
- •4.21 Recent IDL Extensions
- •4.22 Summary
- •Chapter 5. IDL for a Climate Control System
- •5.1 Chapter Overview
- •5.2 The Climate Control System
- •5.3 IDL for the Climate Control System
- •5.4 The Complete Specification
- •Chapter 6. Basic IDL-to-C++ Mapping
- •6.1 Chapter Overview
- •6.2 Introduction
- •6.3 Mapping for Identifiers
- •6.4 Mapping for Modules
- •6.5 The CORBA Module
- •6.6 Mapping for Basic Types
- •6.7 Mapping for Constants
- •6.8 Mapping for Enumerated Types
- •6.9 Variable-Length Types and _var Types
- •6.10 The String_var Wrapper Class
- •6.11 Mapping for Wide Strings
- •6.12 Mapping for Fixed-Point Types
- •6.13 Mapping for Structures
- •6.14 Mapping for Sequences
- •6.15 Mapping for Arrays
- •6.16 Mapping for Unions
- •6.17 Mapping for Recursive Structures and Unions
- •6.18 Mapping for Type Definitions
- •6.19 User-Defined Types and _var Classes
- •6.20 Summary
- •Chapter 7. Client-Side C++ Mapping
- •7.1 Chapter Overview
- •7.2 Introduction
- •7.3 Mapping for Interfaces
- •7.4 Object Reference Types
- •7.5 Life Cycle of Object References
- •7.6 Semantics of _ptr References
- •7.7 Pseudo-Objects
- •7.8 ORB Initialization
- •7.9 Initial References
- •7.10 Stringified References
- •7.11 The Object Pseudo-Interface
- •7.12 _var References
- •7.13 Mapping for Operations and Attributes
- •7.14 Parameter Passing Rules
- •7.15 Mapping for Exceptions
- •7.16 Mapping for Contexts
- •7.17 Summary
- •Chapter 8. Developing a Client for the Climate Control System
- •8.1 Chapter Overview
- •8.2 Introduction
- •8.3 Overall Client Structure
- •8.4 Included Files
- •8.5 Helper Functions
- •8.6 The main Program
- •8.7 The Complete Client Code
- •8.8 Summary
- •Chapter 9. Server-Side C++ Mapping
- •9.1 Chapter Overview
- •9.2 Introduction
- •9.3 Mapping for Interfaces
- •9.4 Servant Classes
- •9.5 Object Incarnation
- •9.6 Server main
- •9.7 Parameter Passing Rules
- •9.8 Raising Exceptions
- •9.9 Tie Classes
- •9.10 Summary
- •Chapter 10. Developing a Server for the Climate Control System
- •10.1 Chapter Overview
- •10.2 Introduction
- •10.3 The Instrument Control Protocol API
- •10.4 Designing the Thermometer Servant Class
- •10.5 Implementing the Thermometer Servant Class
- •10.6 Designing the Thermostat Servant Class
- •10.7 Implementing the Thermostat Servant Class
- •10.8 Designing the Controller Servant Class
- •10.9 Implementing the Controller Servant Class
- •10.10 Implementing the Server main Function
- •10.11 The Complete Server Code
- •10.12 Summary
- •Chapter 11. The Portable Object Adapter
- •11.1 Chapter Overview
- •11.2 Introduction
- •11.3 POA Fundamentals
- •11.4 POA Policies
- •11.5 POA Creation
- •11.6 Servant IDL Type
- •11.7 Object Creation and Activation
- •11.8 Reference, ObjectId, and Servant
- •11.9 Object Deactivation
- •11.10 Request Flow Control
- •11.11 ORB Event Handling
- •11.12 POA Activation
- •11.13 POA Destruction
- •11.14 Applying POA Policies
- •11.15 Summary
- •Chapter 12. Object Life Cycle
- •12.1 Chapter Overview
- •12.2 Introduction
- •12.3 Object Factories
- •12.4 Destroying, Copying, and Moving Objects
- •12.5 A Critique of the Life Cycle Service
- •12.6 The Evictor Pattern
- •12.7 Garbage Collection of Servants
- •12.8 Garbage Collection of CORBA Objects
- •12.9 Summary
- •Part III: CORBA Mechanisms
- •Chapter 13. GIOP, IIOP, and IORs
- •13.1 Chapter Overview
- •13.2 An Overview of GIOP
- •13.3 Common Data Representation
- •13.4 GIOP Message Formats
- •13.5 GIOP Connection Management
- •13.6 Detecting Disorderly Shutdown
- •13.7 An Overview of IIOP
- •13.8 Structure of an IOR
- •13.9 Bidirectional IIOP
- •13.10 Summary
- •14.1 Chapter Overview
- •14.2 Binding Modes
- •14.3 Direct Binding
- •14.4 Indirect Binding via an Implementation Repository
- •14.5 Migration, Reliability, Performance, and Scalability
- •14.6 Activation Modes
- •14.7 Race Conditions
- •14.8 Security Considerations
- •14.9 Summary
- •Part VI: Dynamic CORBA
- •Chapter 15 C++ Mapping for Type any
- •15.1 Chapter Overview
- •15.2 Introduction
- •15.3 Type any C++ Mapping
- •15.4 Pitfalls in Type Definitions
- •15.5 Summary
- •Chapter 16. Type Codes
- •16.1 Chapter Overview
- •16.2 Introduction
- •16.3 The TypeCode Pseudo-Object
- •16.4 C++ Mapping for the TypeCode Pseudo-Object
- •16.5 Type Code Comparisons
- •16.6 Type Code Constants
- •16.7 Type Code Comparison for Type any
- •16.8 Creating Type Codes Dynamically
- •16.9 Summary
- •Chapter 17. Type DynAny
- •17.1 Chapter Overview
- •17.2 Introduction
- •17.3 The DynAny Interface
- •17.4 C++ Mapping for DynAny
- •17.5 Using DynAny for Generic Display
- •17.6 Obtaining Type Information
- •17.7 Summary
- •Part V: CORBAservices
- •Chapter 18. The OMG Naming Service
- •18.1 Chapter Overview
- •18.2 Introduction
- •18.3 Basic Concepts
- •18.4 Structure of the Naming Service IDL
- •18.5 Semantics of Names
- •18.6 Naming Context IDL
- •18.7 Iterators
- •18.8 Pitfalls in the Naming Service
- •18.9 The Names Library
- •18.10 Naming Service Tools
- •18.11 What to Advertise
- •18.12 When to Advertise
- •18.13 Federated Naming
- •18.14 Adding Naming to the Climate Control System
- •18.15 Summary
- •Chapter 19. The OMG Trading Service
- •19.1 Chapter Overview
- •19.2 Introduction
- •19.3 Trading Concepts and Terminology
- •19.4 IDL Overview
- •19.5 The Service Type Repository
- •19.6 The Trader Interfaces
- •19.7 Exporting Service Offers
- •19.8 Withdrawing Service Offers
- •19.9 Modifying Service Offers
- •19.10 The Trader Constraint Language
- •19.11 Importing Service Offers
- •19.12 Bulk Withdrawal
- •19.13 The Admin Interface
- •19.14 Inspecting Service Offers
- •19.15 Exporting Dynamic Properties
- •19.16 Trader Federation
- •19.17 Trader Tools
- •19.18 Architectural Considerations
- •19.19 What to Advertise
- •19.20 Avoiding Duplicate Service Offers
- •19.21 Adding Trading to the Climate Control System
- •19.22 Summary
- •Chapter 20. The OMG Event Service
- •20.1 Chapter Overview
- •20.2 Introduction
- •20.3 Distributed Callbacks
- •20.4 Event Service Basics
- •20.5 Event Service Interfaces
- •20.6 Implementing Consumers and Suppliers
- •20.7 Choosing an Event Model
- •20.8 Event Service Limitations
- •20.9 Summary
- •Part VI: Power CORBA
- •Chapter 21. Multithreaded Applications
- •21.1 Chapter Overview
- •21.2 Introduction
- •21.3 Motivation for Multithreaded Programs
- •21.4 Fundamentals of Multithreaded Servers
- •21.5 Multithreading Strategies
- •21.6 Implementing a Multithreaded Server
- •21.7 Servant Activators and the Evictor Pattern
- •21.8 Summary
- •22.1 Chapter Overview
- •22.2 Introduction
- •22.3 Reducing Messaging Overhead
- •22.4 Optimizing Server Implementations
- •22.5 Federating Services
- •22.6 Improving Physical Design
- •22.7 Summary
- •Appendix A. Source Code for the ICP Simulator
- •Appendix B. CORBA Resources
- •Bibliography
IT-SC book: Advanced CORBA® Programming with C++
Find a set of steel radial tires on offer in the San Francisco Bay area with a speed rating of at least 120 m.p.h., size P205/65R15, made by either Bridgestone or Goodyear. Make sure that either Visa or MasterCard is accepted for payment.
We can express this query as a constraint as follows:
Location == 'San Francisco Bay' and Speed >= 120
and Size == 'P205/65R15'
and (Manufacturer == 'Bridgestone' or Manufacturer == 'Goodyear') and ('Visa' in CreditCards or 'MasterCard' in CreditCards)
The constraint looks like an SQL where clause. The specification makes this choice to permit implementations of traders to directly use SQL database back ends. Note that a constraint string can be split over several lines (white space and indentation are not significant except to separate tokens).
19.11 Importing Service Offers
The trader allows importers detailed control over how a trader is to search for matching service offers and how these service offers should be returned. As a result, the import operation has a large number of parameters. Again, we discuss the relevant IDL definition and then show a number of examples of importing service offers from C++.
19.11.1 IDL for the Lookup Interface
Importers use the Lookup interface to import service offers. The Lookup interface has only a single operation, query:[4]
[4] It is a little unfortunate that the export operation is called export but the import operation is called query. The authors would have preferred more consistent naming of operations.
typedef Istring |
Constraint; |
typedef string |
PolicyName; |
typedef sequence<PolicyName> |
PolicyNameSeq; |
typedef any |
PolicyValue; |
struct Policy { PolicyName name; PolicyValue value;
};
typedef sequence<Policy> PolicySeq;
struct Offer {
Object reference; PropertySeq properties;
};
typedef sequence<Offer> OfferSeq;
752
IT-SC book: Advanced CORBA® Programming with C++
interface OfferIterator; |
// Forward declaration |
exception IllegalConstraint { Constraint constr;
};
exception DuplicatePolicyName { PolicyName name;
};
interface Lookup :
TraderComponents, SupportAttributes, ImportAttributes {
enum HowManyProps { none, some, all };
union SpecifiedProps switch (HowManyProps) { case some:
PropertyNameSeq prop_names;
};
typedef Istring Preference; exception IllegalPreference {
Preference pref;
}; |
|
|
exception IllegalPolicyName { |
|
|
}; |
PolicyName name; |
|
|
|
|
exception PolicyTypeMismatch { |
|
|
}; |
Policy the_policy; |
|
|
|
|
exception InvalidPolicyValue { |
|
|
}; |
Policy the_policy; |
|
|
|
|
void |
query( |
type, |
|
in ServiceTypeName |
|
|
in Constraint |
constr, |
|
in Preference |
pref, |
|
in PolicySeq |
policies, |
|
in SpecifiedProps |
desired_props, |
|
in unsigned long |
how_many, |
|
out OfferSeq |
offers, |
|
out OfferIterator |
offer_itr, |
|
out PolicyNameSeq |
limits_applied |
|
) raises( |
|
IllegalServiceType, UnknownServiceType,
IllegalConstraint, IllegalPreference,
IllegalPolicyName, PolicyTypeMismatch,
InvalidPolicyValue, IllegalPropertyName,
DuplicatePropertyName, DuplicatePolicyName
);
};
753
IT-SC book: Advanced CORBA® Programming with C++
The query operation has six in parameters and three out parameters. (As a matter of style, the authors would have preferred several versions of the query operation instead of a single Swiss army knife operation that offers all possible options.) The parameters are as follows.
type
The type parameter nominates the service type for the query. The trader considers offers of the nominated type and types derived from the nominated type as eligible for matching against the constraint.
constr
This parameter specifies the constraint string to be used. The constraint string can be the empty string, which means the same thing as TRUE.
pref
This parameter allows you to specify a preference for a query (see Section 19.11.5). You can pass an empty string as the pref parameter, in which case the trader uses the default preference.
policies
This parameter specifies the import policies that should be applied to the query. You can pass an empty sequence as the policies parameter, in which case the trader uses the configured default policies.
desired_props
This parameter specifies the policy values to be returned for each matching service offer. You can select to get only the object reference to the service provider or select to also get all or a specified subset of the property values of each matching service offer.
how_many
This parameter is analogous to its use in the Naming Service. It specifies the maximum number of matching service offers to be returned from the query operation.
offers
The offers parameter returns a sequence of service offers that match the constraint.
offer_itr
If a query returns a large number of service offers, this out parameter contains a reference to an OfferIterator object that you can use to incrementally retrieve the remainder of the result.
limits_applied
During evaluation of the query, a trader can apply certain limits. For example, the trader can limit the search space to a certain number of offers. The limits_applied out parameter returns the names of the policies that were used to limit the query.
The query operation can raise a number of exceptions. We discuss some of them in the preceding sections. Here are the exceptions that are new.
754
IT-SC book: Advanced CORBA® Programming with C++
IllegalConstraint
The constraint string passed to the query is syntactically malformed or contains a semantic error (such as comparing a string for equality with a number).
DuplicatePolicyName
The policies parameter contains two or more elements that have the same policy name.
IllegalPreference
The string passed in the pref parameter is syntactically invalid or contains a semantic error.
IllegalPolicyName
The policies parameter contains a policy name that is syntactically malformed or is not recognized by the trader.
PolicyTypeMismatch
A policy value has a type that does not match the expected type for that policy.
InvalidPolicyValue
A policy value is out of range or otherwise considered meaningless.
19.11.2 Writing a Simple Query
Following is a simple query to locate a service offer for a controller.
using namespace CosTrading;
// Get reference to Lookup interface. Lookup_var lookup;
lookup = resolve_init<CosTrading::Lookup>(orb, "Tradi ngService");
PolicySeq policies; |
|
// Empty sequence |
Lookup::SpecifiedProps desired_props; |
// Don't return properties |
|
desired_props._default(); |
|
|
desired_props._d(Lookup::none); |
|
|
PolicyNameSeq_var policies_applied; |
// out param |
|
OfferSeq_var |
offers; |
// out param |
OfferIterator_var |
iterator; |
// out param |
//Run query without preferences using default policies. lookup->query(
"CCS::Controllers", "TRUE", "", policies, desired_props, 1, offers, iterator, policies_applied
);
//Process results.
CCS::Controller_var ctrl; if (offers->length() == 0) {
755
IT-SC book: Advanced CORBA® Programming with C++
cout < "No matching service offer." < endl;
}else {
//Extract controller reference from returned offer. ctrl = CCS::Controller::_narrow(offers[0].reference); if (CORBA::is_nil(ctrl)) {
cerr < "Service provider is not a controller!" < endl;
throw 0;
}
}
// Clean up
if (!CORBA::is_nil(iterator)) iterator->destroy();
// Use controller...
This code goes through the following steps:
Step 1.
Get a Lookup reference from resolve_initial_references. (We use the resolve_init template function defined in Section 18.14.1.)
Step 2.
Initialize a SpecifiedProps union. For this example, we set the discriminator to none, which indicates that we do not want property values to be returned.
Step 3.
Invoke the query operation. We specify "CCS::Controllers" as the service type and "TRUE" as the constraint, so any controller at all will match the constraint. The third parameter is an empty string (indicating that the default preferences apply), and the fourth parameter is an empty policy sequence (indicating that the default policies apply). The desired_props parameter, initialized in step 2, indicates that no property values are to be returned. The how_many parameter is 1, and that guarantees that the sequence of matching offers returned in the offers parameter will contain no more than one service offer.
Step 4.
After the call completes, the code checks the length of the returned offer sequence. If the sequence is empty, no matching controllers were found. Otherwise, the offer sequence contains exactly one element (because how_many was set to 1 for the call) and the code narrows the reference contained in the service offer to the CCS::Controller type. The actual type of the reference may be derived from CCS::Controller; if it is, the _narrow call still succeeds.
Step 5.
The trader may have created an iterator to hold other matching service offers. If it has, the code immediately destroys the iterator because it is not interested in any other matching service offers.
19.11.3 The OfferIterator Interface
756
IT-SC book: Advanced CORBA® Programming with C++
We may be interested in finding all controllers that match the constraint instead of only a single one. As with the Naming Service, this creates the problem of how to return result sets of arbitrary size from an operation. The trader uses an iterator interface that is similar, but unfortunately not identical, to that of the Naming Service. Here are the semantics of the how_many parameter and the OfferIterator interface.
The offer sequence returned by query contains no more than how_many elements. If how_many is set to zero, the returned offer sequence is guaranteed to be empty and results must be retrieved via the iterator.
The trader may return fewer than how_many offers in the offer sequence. If how_many is non-zero, the offer sequence is empty only if there are no matching results. If how_many is zero, you use the returned iterator (if any) to determine how many results there are.
If not all matching offers are returned in the offer sequence, the offer_itrout parameter is used to retrieve the remaining offers.
The OfferIterator interface is specified by the following IDL.
// In module CosTrading...
exception UnknownMaxLeft {};
interface OfferIterator {
boolean next_n(in unsigned long n, out OfferSeq offers); unsigned long max_left() raises(UnknownMaxLeft);
void destroy();
};
The next_n operation returns the next batch of no more than n matching offers in the offers parameter. As with query, fewer than n offers may be returned (but offers is guaranteed to always contain as least one offer). The return value is true if further offers are to be retrieved. A return value of false indicates that this invocation of next_n returned the final batch of offers; that is, the offers parameter will contain at least one matching offer even when the return value is false. Calling next_n after it has returned false has undefined behavior.
The max_left operation indicates how many offers are still remaining. If that determination cannot be made, the operation raises the UnknownMaxLeft exception. (We recommend that you do not use max_left. Because of the lazy evaluation used by most trader implementations, it is highly likely that it will raise UnknownMaxLeft whenever it is called.)
The destroy operation destroys the iterator. You can call destroy at any time, even before you have retrieved all results, but you must call destroy even if you do retrieve all results. If you don't call destroy, you will leave an abandoned object in the trader—
757
IT-SC book: Advanced CORBA® Programming with C++
see page 807. The trader is free to destroy stale iterators if it is about to run out of resources, so you must be prepared to handle OBJECT_NOT_EXIST exceptions from iterator operations. As with the Naming Service, you should retrieve the results promptly and not hold on to iterators for longer than absolutely necessary.
Using the OfferIterator Interface with C++
The next_n operation returns false with the last batch of results. This means that you must be careful how you write the code to retrieve service offers from the iterator. The following does not work correctly:
OfferIterator_var iter;
//Get iterator from query operation...
//Process remaining results.
while (iter->next_n(50, offers)) { |
// WRONG! |
// Process offers... |
|
} |
|
This code does not work correctly because it will miss processing for the final batch of offers returned by next_n.
There are two options for structuring the code so that it correctly processes all the results. The first option is to use a post-tested loop after processing the first batch:
//Run query. lookup->query(
service_type, constraint, preferences, policies, desired_props, how_many, offers, iter, policies_applied
);
//Process first batch.
for (CORBA::ULong i = 0; i < offers->length(); i++) { // Process offer...
}
// Process remaining offers. if (!CORBA::is_nil(iter)) { CORBA::Boolean more;
do {
more = iter->next_n(how_many, offers);
for (CORBA::ULong i = 0; i < offers->length(); i++) { // Process offer...
} |
|
} while (more); |
// Clean up |
iter->destroy(); |
}
Because the code uses a post-tested loop, the final batch of offers is processed correctly.
758
IT-SC book: Advanced CORBA® Programming with C++
The second option is to call query with a how_many value of zero and to retrieve all offers via the iterator:
// Run query. |
|
lookup->query( |
|
service_type, constraint, preferences, |
// how_many == 0 |
policies, desired_props, 0, |
|
offers, iter, policies_applied |
|
); |
|
if (!CORBA::is_nil(iter)) { CORBA::Boolean more; do {
// Get next batch of offers.
more = iter->next_n(how_many, offers);
for (CORBA::ULong i = 0; i < offers->length(); i++)
//Process offer...
}while (more);
iter->destroy(); |
// Clean up |
}
This version is simpler than the other one but requires the less than obvious call to query with a zero value for how_many.
A Few Words about Iterator Design
Let us step back and examine this iterator design for a moment. Instead of returning false after the last batch of offers is returned, next_n returns false with the last batch. The motivation for this design is to save a remote call. Because next_n returns false with the last batch, the client need not make an additional call that returns no offers just to get the end-of-offers indication.
What are the consequences of this design? For one thing, these iterator semantics force the trader to read ahead by at least one service offer during calls to next_n (otherwise, the operation cannot return the correct value). This in itself can be a problem, in particular if a trader is a front end to a legacy system that offers only a simple streaming interface. If that streaming interface can deliver offers only in batches and does not provide a seek facility, the iterator must buffer the undelivered service offers between calls to next_n.
Second, the iterator interface is substantially more complex and is harder for a client programmer to interact with correctly. The design leads to an interface in which the most obvious approach of using a while loop (as shown on page 880) does precisely the wrong thing.
The iterator design accepts additional complexity to save one remote call per query. Are the gains worth the pain? Almost certainly not. Consider this: if a client submits a query that delivers a large number of results and must use an iterator, there are two possible styles of interaction with the iterator.
759
IT-SC book: Advanced CORBA® Programming with C++
The client can use a very small value for how_many, in which case the client will make many calls to next_n to retrieve the complete set of results. The main cost of iteration is incurred by the call dispatch overhead for each individual call.
The client can use a large value for how_many, in which case the client will make few calls to next_n to retrieve the complete set of results. The main cost of iteration is incurred by the amount of bandwidth required to marshal the large result batches to the client.
In either case, the savings of this iterator design (a single remote call) become vanishingly small if more than a dozen or so service offers are returned.
The moral is that you must be careful about whether you allow efficiency considerations to impinge on IDL interfaces. The design of the offer iterator in the trader is a classic case in which a wrong decision was made. The very small gain in efficiency does not justify the additional complexity of the interface. In general, we recommend that you create iterators that are more along the lines of the Naming Service because of the simplicity of that design.
19.11.4 Controlling Query Result Details
Here again is the IDL for the returned service offers:
// ...
typedef Istring PropertyName; typedef any PropertyValue;
struct Property { PropertyName name; PropertyValue value;
};
typedef sequence<Property> PropertySeq;
struct Offer {
Object reference; PropertySeq properties;
};
typedef sequence<Offer> OfferSeq;
The query in Section 19.11.2 uses a discriminator value of none for the desired_props parameter. With that value, each returned offer contains the object reference of the service provider and an empty properties sequence. We can use the desired_props parameter to control which property values are returned for each service offer. Here again is the IDL for the corresponding union:
interface Lookup :
TraderComponents, SupportAttributes, ImportAttributes {
760
IT-SC book: Advanced CORBA® Programming with C++
enum HowManyProps { none, some, all };
union SpecifiedProps switch (HowManyProps) { case some:
PropertyNameSeq prop_names;
}; // ...
};
If we set the discriminator value of this union to all, then for each returned service offer, the properties member contains all the properties for the offer. In addition, with a discriminator value of some, we can specify exactly which property values are to be returned. Here is a code fragment that imports controller offers and explicitly requires that the Model and Manufacturer properties are to be returned:
// ...
PropertyNameSeq pnames; pnames.length(2);
pnames[0] = CORBA::string_dup("Model"); pnames[1] = CORBA::string_dup("Manufacturer");
Lookup::SpecifiedProps desired_props; desired_props.prop_names(pnames);
// Run query...
Passing this union to the query ensures that each returned service offer contains the specified properties as a name-value pair of type Property. If a property is optional and a matching service offer does not contain that property, the property value will be missing from the returned properties sequence member for that offer.
Why would you bother retrieving property values in addition to the IOR of the service provider? Here are some reasons.
You may want to select offers based on properties of user-defined type, but the constraint language does not permit you to use user-defined types in expressions. By asking for the property values, you can post-filter the results from a query based on the contents of userdefined types.
You may want to apply an operator that is not directly supported by the query language, such as taking the square root of a property value. Retrieving property values allows you to post-filter results with operators that are not directly supported.
You may want to select a number of service offers based on a constraint and then present the matching service offers and their property values to a user for final selection.
761
IT-SC book: Advanced CORBA® Programming with C++
19.11.5 Using Preferences
The pref parameter passed to the query operation allows you to control the order in which results are returned to you. You can pass an empty string as the pref parameter. Otherwise, the pref parameter is a string with exactly one of the following values.
first
A preference value of first indicates that the trader should return offers in whatever order is most convenient (typically, the order in which they are discovered). This is the default if an empty string is passed.
random
Service offers are randomized. The trader first retrieves all matching service offers and then "shuffles" the offer sequence before returning it. This preference is useful if clients have a number of equivalent services to choose from. Randomizing implements a simple form of load balancing across these services.
min expr
The service offers are returned in increasing order of expr. For example, a preference string of "min Price" returns service offers with the lowest price first.
max expr
The service offers are returned in decreasing order of expr. For example, a preference string of "max MaxDevices" returns service offers with the largest value for the MaxDevices property first.
with expr
The expression for the with preference must be a valid constraint expression. Service offers that match the constraint expression are returned before service offers that do not match it. For example, the preference string "with '90' ~ Model" returns all controllers that have the string "90" as part of the model description before returning other matching controllers.
Instead of using a simple property name, you can use more complex expressions for the min, max, and with preferences. For example, the preference
min (12.3 mem_size + 4.6 * file_size)
selects service offers based on the optimization of a weighted function over property values that determine memory and disk requirements.
19.11.6 Import Policies
Import policies allow control over a few non-functional aspects of queries. Here are the semantics of the import policies.
762
IT-SC book: Advanced CORBA® Programming with C++
search_card
This policy determines the maximum number of offers to be searched. match_card
This policy determines the maximum number of offers to be ordered or randomized and affects the amount of buffer space required during query evaluation.
return_card
This policy determines the maximum number of offers to be returned from a query. max_list
This policy determines the maximum number of offers to be returned by a single call to query or next_n.
exact_type_match
This Boolean policy determines whether the trader will consider offers of a derived service type eligible for matching. If you set this policy to true, the matching of derived service offers is disabled.
use_modifiable_properties
If this Boolean policy is false, the trader will ignore all offers that contain modifiable properties even if they would otherwise match the constraint. use_dynamic_properties
If this Boolean policy is false, the trader will ignore all offers that contain dynamic properties even if they would otherwise match the constraint.
use_proxy_offers
If this Boolean policy is false, the trader will ignore all proxy offers even if they would otherwise match the constraint.
follow_policy
This policy controls whether the query will be passed to federated traders. The policy value is of enumerated type with the following values.
local_only
The trader will not pass the query to federated traders for evaluation. if_no_local
The trader first searches its own offer space. If the search locates one or more matching offers, the query returns those offers. If the search does not locate matching offers locally, the query is passed to federated traders for evaluation and returns whatever matching offers are found in the federated traders.
always
If the trader is federated, it will pass the query to the federated traders as well as search its own offer space.
You can set each import policy value in the policies parameter of a query operation. For the search_card, match_card, return_card, and follow_policy policies, each trader defines a traderwide limiting value as well as a default value that applies if the policy is not explicitly specified for a query. If you set a policy value that is more permissive or larger than the trader's limit, the limit is silently applied to the query. You can read the limiting and default values from attributes in the ImportAttributes interface (see page 858).
The max_list policy does not have a default value; it has only a limiting value, which is also available on the ImportAttributes interface. A call to query or next_n
763
IT-SC book: Advanced CORBA® Programming with C++
never returns more than max_list service offers regardless of the value of the how_many parameter.
We recommend that you do not set exact_type_match to true unless you are maintaining a trader's offer space. Setting this policy to true disables polymorphism and defeats the object-oriented nature of CORBA.
Using Policies with C++
Here is a code example that initializes a policy sequence to pass to the query operation:
using namespace CosTrading;
// ...
PolicySeq policies; policies.length(3);
policies[0].name = CORBA::string_dup("search_card"); policies[0].value <= lookup->max_search_card(); policies[1].name = CORBA::string_dup("match_card"); policies[1].value <= lookup->max_match_card(); policies[2].name = CORBA::string_dup("return_card"); policies[2].value <= lookup->max_return_card();
Lookup::SpecifiedProps desired_props; |
// Don't return properties |
|
desired_props._default(); |
|
|
desired_props._d(Lookup::none); |
|
|
PolicyNameSeq_var policies_applied; |
// out param |
|
OfferSeq_var |
offers; |
// out param |
OfferIterator_var |
iterator; |
// out param |
//Run query without using specified policies. lookup->query(
"CCS::Controllers", "TRUE", "min Price", policies, desired_props, how_many, offers, iterator, policies_applied
);
//Process results...
This code sets the search_card, match_card, and return_card policies to their maximum permissible values by reading these values from the ImportAttributes interface (which is a base interface of Lookup). The policy sequence is a list of namevalue pairs to which the code assigns the policy name and its value.
Policy Limits
During the evaluation of a query, a trader can apply limits. For example, the query can stop searching for matching service offers when it reaches the configured or explicitly specified limit of the search_card policy. If a trader applies limits to a query, the limits_applied out parameter returned by the query contains the names of the
764
IT-SC book: Advanced CORBA® Programming with C++
policies that limit query evaluation. For example, the preceding query might return the following values in the limits_applied parameter:
search_card match_card return_card
Unfortunately, the limits_applied parameter is not very useful because it contains only a list of policy names. Therefore, by looking at this parameter, you can learn only that a trader applied a limit and cannot learn what the value of the limit was or which trader applied it.
Choosing Policies and Preferences
Policy values have performance implications. Traders are typically tuned to perform best if no overriding policy values are supplied by the importer, so you should not change the default policies (except for return_card) unless you have a good reason. Also, policy values are advisory only. A trader is free to ignore one or more policy values—for example, because database limits prevent the policy value from being used as specified.
Preferences can also lower performance. For example, if you submit a query that returns a large number of offers and also requires ordering or randomizing of the results, the trader is forced to allocate sufficient buffer space to hold the complete query result, and that has implications for performance and memory consumption.
It is possible to specify combinations of policy values that make little sense, such as match_card > search_card and return_card > match_card. The specification does not define how a trader should deal with such policy combinations: a trader may ignore a meaningless combination, may apply the most restrictive policy, or may follow the policies blindly (something that will likely reduce performance).
Even with sensible policy values, a particular query result may cause problems. For example, for sorting of offers, the match_card policy limits the number of service offers that will be sorted. However, because service offers can vary in size, the amount of buffer space required for sorting may be too large for a particular result set. In this case, the trader returns however many service offers it can fit into memory in sorted order and returns the remaining offers in unsorted order.
Here are a few policy combinations that are commonly used.
The most common use of a trader is for a simple import, in which the importer is interested only in a single matching service offer and does not have a notion of "best" or "cheapest." In this case, set return_card and match_card to 1 and use the default preference of first (an empty preference string is the same as specifying first explicitly). These settings permit the trader to do the minimum amount of work to retrieve a matching service offer. In addition, most traders are optimized for this case.
765
