Ice 3.4.2 Documentation


Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1. Ice Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.1 Ice Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.1.1 Ice Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.1.1.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.1.1.2 Slice (Specification Language for Ice) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.1.1.3 Language Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.1.1.4 Client and Server Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.1.1.5 Overview of the Ice Protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 1.1.2 Ice Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 1.1.3 Architectural Benefits of Ice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1.2 Hello World Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 1.2.1 Writing a Slice Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 1.2.2 Writing an Ice Application with C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 1.2.3 Writing an Ice Application with Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 1.2.4 Writing an Ice Application with C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 1.2.5 Writing an Ice Application with Visual Basic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 1.2.6 Writing an Ice Application with Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 1.2.7 Writing an Ice Application with Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 1.2.8 Writing an Ice Application with Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 1.2.9 Writing an Ice Application with PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 1.3 The Slice Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 1.3.1 Slice Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 1.3.2 Slice Source Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 1.3.3 Lexical Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 1.3.4 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 1.3.5 Basic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 1.3.6 User-Defined Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 1.3.6.1 Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 1.3.6.2 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 1.3.6.3 Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 1.3.6.4 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 1.3.6.5 Constants and Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 1.3.7 Interfaces, Operations, and Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 1.3.7.1 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 1.3.7.2 User Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 1.3.7.3 Run-Time Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 1.3.7.4 Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 1.3.7.5 Interface Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 1.3.8 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 1.3.8.1 Simple Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 1.3.8.2 Class Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 1.3.8.3 Class Inheritance Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 1.3.8.4 Classes as Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 1.3.8.5 Self-Referential Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 1.3.8.6 Classes Versus Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 1.3.8.7 Classes with Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 1.3.8.8 Architectural Implications of Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 1.3.8.9 Classes Implementing Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 1.3.8.10 Class Inheritance Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 1.3.8.11 Pass-by-Value Versus Pass-by-Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 1.3.8.12 Passing Interfaces by Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 1.3.9 Forward Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 1.3.10 Type IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 1.3.11 Operations on Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 1.3.12 Local Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 1.3.13 Names and Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 1.3.14 Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 1.3.15 Serializable Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 1.3.16 Deprecating Slice Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 1.3.17 Using the Slice Compilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 1.3.18 Slice Checksums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 1.3.19 Generating Slice Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 1.3.20 Slice Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 1.3.21 Slice Metadata Directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 1.3.22 Slice for a Simple File System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 1.4 C++ Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 1.4.1 Client-Side Slice-to-C++ Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 1.4.1.1 C++ Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 1.4.1.2 C++ Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 1.4.1.3 C++ Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 1.4.1.4 C++ Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 1.4.1.5 C++ Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 1.4.1.6 C++ Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 1.4.1.7 C++ Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 1.4.1.8 C++ Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.4.1.9 C++ Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 1.4.1.10 C++ Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 1.4.1.11 C++ Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 1.4.1.12 C++ Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 1.4.1.13 Smart Pointers for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 1.4.1.14 Asynchronous Method Invocation (AMI) in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 1.4.1.15 slice2cpp Command-Line Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 1.4.1.16 Using Slice Checksums in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 1.4.1.17 Example of a File System Client in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 1.4.2 Server-Side Slice-to-C++ Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 1.4.2.1 The Server-Side main Function in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 1.4.2.2 Server-Side C++ Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 1.4.2.3 Parameter Passing in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 1.4.2.4 Raising Exceptions in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 1.4.2.5 Object Incarnation in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 1.4.2.6 Asynchronous Method Dispatch (AMD) in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 1.4.2.7 Example of a File System Server in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 1.4.3 The C++ Utility Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 1.4.3.1 The C++ AbstractMutex Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 1.4.3.2 The C++ Cache Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 1.4.3.3 The C++ Exception Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 1.4.3.4 The C++ generateUUID Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 1.4.3.5 The C++ Handle Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 1.4.3.6 The C++ Handle Template Adaptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 1.4.3.7 The C++ ScopedArray Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 1.4.3.8 The C++ Shared and SimpleShared Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 1.4.3.9 The C++ Time Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 1.4.3.10 The C++ Timer and TimerTask Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 1.4.3.11 Unicode and UTF-8 Conversion Functions in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 1.4.3.12 Version Information in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 1.5 Java Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 1.5.1 Client-Side Slice-to-Java Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 1.5.1.1 Java Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 1.5.1.2 Java Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 1.5.1.3 Java Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317 1.5.1.4 Java Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318 1.5.1.5 Java Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319 1.5.1.6 Java Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 1.5.1.7 Java Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 1.5.1.8 Java Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 1.5.1.9 Java Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 1.5.1.10 Java Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 1.5.1.11 Java Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 1.5.1.12 Java Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342 1.5.1.13 Serializable Objects in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 1.5.1.14 Customizing the Java Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350 1.5.1.15 Asynchronous Method Invocation (AMI) in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 1.5.1.16 Using the Slice Compiler for Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 1.5.1.17 Using Slice Checksums in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372 1.5.1.18 Example of a File System Client in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 1.5.2 Server-Side Slice-to-Java Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 1.5.2.1 The Server-Side main Method in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 1.5.2.2 Server-Side Java Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383 1.5.2.3 Parameter Passing in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 1.5.2.4 Raising Exceptions in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387 1.5.2.5 Tie Classes in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388 1.5.2.6 Object Incarnation in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 1.5.2.7 Asynchronous Method Dispatch (AMD) in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 1.5.2.8 Example of a File System Server in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 1.5.3 The Java Utility Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 1.6 C-Sharp Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 1.6.1 Client-Side Slice-to-C-Sharp Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 1.6.1.1 C-Sharp Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 1.6.1.2 C-Sharp Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 1.6.1.3 C-Sharp Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 1.6.1.4 C-Sharp Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 1.6.1.5 C-Sharp Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 1.6.1.6 C-Sharp Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 1.6.1.7 C-Sharp Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 1.6.1.8 C-Sharp Collection Comparison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 1.6.1.9 C-Sharp Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 1.6.1.10 C-Sharp Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 1.6.1.11 C-Sharp Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 1.6.1.12 C-Sharp Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442 1.6.1.13 C-Sharp Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.6.1.14 Serializable Objects in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 1.6.1.15 C-Sharp Specific Metadata Directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458 1.6.1.16 Asynchronous Method Invocation (AMI) in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 1.6.1.17 slice2cs Command-Line Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 1.6.1.18 Using Slice Checksums in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 1.6.1.19 Example of a File System Client in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 1.6.2 Server-Side Slice-to-C-Sharp Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476 1.6.2.1 The Server-Side main Method in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477 1.6.2.2 Server-Side C-Sharp Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 1.6.2.3 Parameter Passing in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485 1.6.2.4 Raising Exceptions in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 1.6.2.5 Tie Classes in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 1.6.2.6 Object Incarnation in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 1.6.2.7 Asynchronous Method Dispatch (AMD) in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 1.6.2.8 Example of a File System Server in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 1.6.3 .NET Compact Framework Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506 1.6.4 The .NET Utility Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508 1.7 Objective-C Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 1.7.1 Client-Side Slice-to-Objective-C Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 1.7.1.1 Objective-C Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 1.7.1.2 Objective-C Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514 1.7.1.3 Objective-C Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 516 1.7.1.4 Objective-C Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 1.7.1.5 Objective-C Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 1.7.1.6 Objective-C Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521 1.7.1.7 Objective-C Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525 1.7.1.8 Objective-C Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 1.7.1.9 Objective-C Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 1.7.1.10 Objective-C Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533 1.7.1.11 Objective-C Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 1.7.1.12 Objective-C Mapping for Local Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546 1.7.1.13 Objective-C Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547 1.7.1.14 Objective-C Mapping for Interfaces by Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 1.7.1.15 Asynchronous Method Invocation (AMI) in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 1.7.1.16 slice2objc Command-Line Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565 1.7.1.17 Example of a File System Client in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566 1.7.2 Server-Side Slice-to-Objective-C Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570 1.7.2.1 The Server-Side main Function in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571 1.7.2.2 Server-Side Objective-C Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 1.7.2.3 Parameter Passing in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 1.7.2.4 Raising Exceptions in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579 1.7.2.5 Object Incarnation in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580 1.7.2.6 Example of a File System Server in Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584 1.8 Python Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 1.8.1 Client-Side Slice-to-Python Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 1.8.1.1 Python Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 1.8.1.2 Python Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598 1.8.1.3 Python Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599 1.8.1.4 Python Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600 1.8.1.5 Python Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602 1.8.1.6 Python Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603 1.8.1.7 Python Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 1.8.1.8 Python Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607 1.8.1.9 Python Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608 1.8.1.10 Python Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 1.8.1.11 Python Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 1.8.1.12 Python Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621 1.8.1.13 Asynchronous Method Invocation (AMI) in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626 1.8.1.14 Code Generation in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634 1.8.1.15 Using Slice Checksums in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642 1.8.1.16 Example of a File System Client in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643 1.8.2 Server-Side Slice-to-Python Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647 1.8.2.1 The Server-Side main Program in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648 1.8.2.2 Server-Side Python Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653 1.8.2.3 Parameter Passing in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655 1.8.2.4 Raising Exceptions in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 1.8.2.5 Object Incarnation in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 1.8.2.6 Asynchronous Method Dispatch (AMD) in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 1.8.2.7 Example of a File System Server in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665 1.9 Ruby Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673 1.9.1 Client-Side Slice-to-Ruby Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674 1.9.1.1 Ruby Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 1.9.1.2 Ruby Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676 1.9.1.3 Ruby Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 1.9.1.4 Ruby Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.9.1.5 Ruby Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 1.9.1.6 Ruby Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681 1.9.1.7 Ruby Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 1.9.1.8 Ruby Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684 1.9.1.9 Ruby Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 1.9.1.10 Ruby Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688 1.9.1.11 Ruby Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693 1.9.1.12 Ruby Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698 1.9.1.13 Code Generation in Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 1.9.1.14 The main Program in Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 1.9.1.15 Using Slice Checksums in Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715 1.9.1.16 Example of a File System Client in Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 1.10 PHP Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 1.10.1 Client-Side Slice-to-PHP Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721 1.10.1.1 PHP Mapping for Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722 1.10.1.2 PHP Mapping for Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723 1.10.1.3 PHP Mapping for Built-In Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724 1.10.1.4 PHP Mapping for Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725 1.10.1.5 PHP Mapping for Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726 1.10.1.6 PHP Mapping for Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727 1.10.1.7 PHP Mapping for Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728 1.10.1.8 PHP Mapping for Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729 1.10.1.9 PHP Mapping for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730 1.10.1.10 PHP Mapping for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 733 1.10.1.11 PHP Mapping for Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739 1.10.1.12 PHP Mapping for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744 1.10.1.13 slice2php Command-Line Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 749 1.10.1.14 Application Notes for PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750 1.10.1.15 Using Slice Checksums in PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756 1.10.1.16 Example of a File System Client in PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757 1.11 Properties and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761 1.11.1 Properties Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 762 1.11.2 Configuration File Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764 1.11.3 Setting Properties on the Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766 1.11.4 Using Configuration Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767 1.11.5 Alternate Property Stores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769 1.11.6 Command-Line Parsing and Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 770 1.11.7 The Properties Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773 1.11.8 Reading Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775 1.11.9 Setting Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776 1.11.10 Parsing Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780 1.12 Threads and Concurrency with C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784 1.12.1 The C++ Mutex Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785 1.12.2 The C++ RecMutex Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791 1.12.3 The C++ Monitor Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793 1.12.4 The C++ Cond Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799 1.12.5 The C++ Thread Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802 1.12.6 Priority Inversion in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810 1.12.7 Portable Signal Handling in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 1.13 The Ice Run Time in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 1.13.1 Communicators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813 1.13.2 Communicator Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 1.13.3 Object Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 1.13.3.1 The Active Servant Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819 1.13.3.2 Creating an Object Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821 1.13.3.3 Servant Activation and Deactivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 1.13.3.4 Object Adapter States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 1.13.3.5 Object Adapter Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 1.13.3.6 Creating Proxies with an Object Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 1.13.3.7 Using Multiple Object Adapters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833 1.13.4 Object Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 1.13.5 The Current Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 837 1.13.6 Servant Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 1.13.6.1 The ServantLocator Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840 1.13.6.2 Threading Guarantees for Servant Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842 1.13.6.3 Registering a Servant Locator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843 1.13.6.4 Servant Locator Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845 1.13.6.5 Using Identity Categories with Servant Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849 1.13.6.6 Using Cookies with Servant Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851 1.13.7 Default Servants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 852 1.13.8 Server Implementation Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856 1.13.9 Servant Evictors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862 1.13.9.1 Implementing a Servant Evictor in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 864 1.13.9.2 Implementing a Servant Evictor in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 870 1.13.9.3 Implementing a Servant Evictor in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.13.10 The Ice Threading Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 883 1.13.10.1 Thread Pools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884 1.13.10.2 Object Adapter Thread Pools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886 1.13.10.3 Thread Pool Design Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 887 1.13.10.4 Nested Invocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 889 1.13.10.5 Thread Safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891 1.13.10.6 Dispatching Invocations to User Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 896 1.13.11 Using Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 907 1.13.11.1 Obtaining Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908 1.13.11.2 Proxy Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911 1.13.11.3 Proxy Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915 1.13.11.4 Filtering Proxy Endpoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 916 1.13.11.5 Proxy Defaults and Overrides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 917 1.13.11.6 Proxy and Endpoint Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 918 1.13.12 Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924 1.13.12.1 Explicit Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925 1.13.12.2 Per-Proxy Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 927 1.13.12.3 Implicit Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 928 1.13.12.4 Design Considerations for Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 930 1.13.13 Connection Timeouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 932 1.13.14 Oneway Invocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 934 1.13.15 Datagram Invocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 937 1.13.16 Batched Invocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939 1.13.17 Locators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941 1.13.17.1 Locator Semantics for Clients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942 1.13.17.2 Locator Configuration for a Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946 1.13.17.3 Locator Semantics for Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947 1.13.17.4 Locator Configuration for a Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 948 1.13.18 Administrative Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 950 1.13.18.1 The admin Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951 1.13.18.2 The Administrative Object Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952 1.13.18.3 Using the admin Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 953 1.13.18.4 The Process Facet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 955 1.13.18.5 The Properties Facet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 958 1.13.18.6 Filtering Administrative Facets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959 1.13.18.7 Custom Administrative Facets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960 1.13.18.8 Security Considerations for Administrative Facets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961 1.13.19 Logger Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962 1.13.19.1 The Default Logger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963 1.13.19.2 Custom Loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964 1.13.19.3 Built-in Loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965 1.13.19.4 Logger Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966 1.13.19.5 The Per-Process Logger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 970 1.13.19.6 C++ Logger Utility Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971 1.13.20 Stats Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973 1.13.21 Location Transparency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975 1.13.22 Automatic Retries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 977 1.13.23 Dispatch Interceptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981 1.13.24 C++ Strings and Character Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 985 1.13.24.1 Installing String Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 986 1.13.24.2 UTF-8 Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 987 1.13.24.3 String Parameters in Local Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 988 1.13.24.4 Built-in String Converters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989 1.13.24.5 String Conversion Convenience Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 990 1.13.24.6 The iconv String Converter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991 1.13.24.7 The Ice String Converter Plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 992 1.13.24.8 Custom String Converter Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993 1.13.25 Plug-in Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994 1.13.25.1 Plug-in API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 995 1.13.25.2 Plug-in Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 997 1.13.25.3 Advanced Plug-in Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998 1.13.26 Custom Class Loaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1000 1.14 Facets and Versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001 1.14.1 Facet Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002 1.14.2 The Versioning Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1008 1.14.3 Versioning with Facets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1012 1.15 Object Life Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016 1.15.1 Understanding Object Life Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017 1.15.2 Object Existence and Non-Existence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1018 1.15.3 Life Cycle of Proxies, Servants, and Ice Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1021 1.15.4 Object Creation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1023 1.15.5 Object Destruction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1027 1.15.5.1 Idempotency and Life Cycle Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029 1.15.5.2 Implementing a destroy Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1030 1.15.5.3 Cleaning Up a Destroyed Servant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.15.5.4 Life Cycle and Collection Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1034 1.15.5.5 Life Cycle and Normal Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1038 1.15.6 Removing Cyclic Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1042 1.15.6.1 Acquiring Locks without Deadlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1043 1.15.6.2 Reaping Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044 1.15.7 Object Identity and Uniqueness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1048 1.15.8 Object Life Cycle for the File System Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1050 1.15.8.1 Implementing Object Life Cycle in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1052 1.15.8.2 Implementing Object Life Cycle in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059 1.15.9 Avoiding Server-Side Garbage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067 1.16 Dynamic Ice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074 1.16.1 Streaming Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1075 1.16.1.1 C++ Streaming Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1076 1.16.1.1.1 The InputStream Interface in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1077 1.16.1.1.2 The OutputStream Interface in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1083 1.16.1.1.3 Intercepting Object Insertion and Extraction in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1088 1.16.1.1.4 Intercepting User Exception Insertion in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089 1.16.1.2 Java Streaming Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1090 1.16.1.2.1 The InputStream Interface in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091 1.16.1.2.2 The OutputStream Interface in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1095 1.16.1.2.3 Stream Helper Functions in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1098 1.16.1.2.4 Intercepting Object Insertion and Extraction in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1100 1.16.1.2.5 Intercepting User Exception Insertion in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101 1.16.1.3 C-Sharp Streaming Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1102 1.16.1.3.1 The InputStream Interface in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1103 1.16.1.3.2 The OutputStream Interface in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107 1.16.1.3.3 Stream Helper Functions in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1110 1.16.1.3.4 Intercepting Object Insertion and Extraction in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1113 1.16.1.3.5 Intercepting User Exception Insertion in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1114 1.16.2 Dynamic Invocation and Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115 1.16.2.1 Dynamic Invocation and Dispatch Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1116 1.16.2.2 Dynamic Invocation and Dispatch in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1119 1.16.2.3 Dynamic Invocation and Dispatch in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124 1.16.2.4 Dynamic Invocation and Dispatch in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1128 1.16.3 Asynchronous Dynamic Invocation and Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1132 1.16.3.1 Asynchronous Dynamic Invocation and Dispatch in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133 1.16.3.2 Asynchronous Dynamic Invocation and Dispatch in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1138 1.16.3.3 Asynchronous Dynamic Invocation and Dispatch in C-Sharp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141 1.17 Connection Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144 1.17.1 Connection Establishment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1145 1.17.2 Active Connection Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1148 1.17.3 Using Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1149 1.17.4 Connection Closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155 1.17.5 Bidirectional Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1156 1.18 The Ice Protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1159 1.18.1 Data Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1160 1.18.1.1 Basic Data Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1161 1.18.1.2 Data Encoding for Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1164 1.18.1.3 Data Encoding for Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1166 1.18.1.3.1 Data Encoding for Class Type IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1167 1.18.1.3.2 Simple Example of Class Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1168 1.18.1.3.3 Data Encoding for Class Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171 1.18.1.4 Data Encoding for Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1176 1.18.1.5 Data Encoding for Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1177 1.18.2 Protocol Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1180 1.18.3 Protocol Compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1186 1.18.4 Protocol and Encoding Versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187 1.19 IceGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1189 1.19.1 IceGrid Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191 1.19.2 Getting Started with IceGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1193 1.19.3 Using IceGrid Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197 1.19.4 Well-Known Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1203 1.19.5 IceGrid Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1209 1.19.6 IceBox Integration with IceGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1213 1.19.7 Object Adapter Replication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1216 1.19.8 Load Balancing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1219 1.19.9 Resource Allocation using IceGrid Sessions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1221 1.19.10 Registry Replication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1226 1.19.11 Application Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1230 1.19.12 IceGrid Administrative Sessions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1236 1.19.13 Glacier2 Integration with IceGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1242 1.19.14 IceGrid XML Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1245 1.19.14.1 Adapter Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1246 1.19.14.2 Allocatable Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1248 1.19.14.3 Application Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1249Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.19.14.4 DbEnv Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1250 1.19.14.5 DbProperty Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251 1.19.14.6 Description Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1252 1.19.14.7 Directory Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1253 1.19.14.8 Distrib Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1254 1.19.14.9 IceBox Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255 1.19.14.10 IceGrid Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1256 1.19.14.11 Load-Balancing Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1257 1.19.14.12 Log Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1258 1.19.14.13 Node Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1259 1.19.14.14 Object Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1260 1.19.14.15 Parameter Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261 1.19.14.16 Properties Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1262 1.19.14.17 Property Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1263 1.19.14.18 Replica-Group Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1264 1.19.14.19 Server Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1265 1.19.14.20 Server-Instance Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267 1.19.14.21 Server-Template Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1268 1.19.14.22 Service Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1269 1.19.14.23 Service-Instance Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1270 1.19.14.24 Service-Template Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271 1.19.14.25 Variable Descriptor Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1272 1.19.14.26 Using Command Line Options in Descriptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1273 1.19.14.27 Setting Environment Variables in Descriptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1274 1.19.15 Using Descriptor Variables and Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1276 1.19.16 IceGrid Property Set Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281 1.19.17 IceGrid XML Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1285 1.19.18 IceGrid Server Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1287 1.19.18.1 icegridregistry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1288 1.19.18.2 icegridnode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290 1.19.18.3 Well-Known Registry Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1292 1.19.18.4 IceGrid Persistent Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293 1.19.18.5 Promoting a Registry Slave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1295 1.19.19 IceGrid and the Administrative Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1296 1.19.20 Securing IceGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1302 1.19.21 IceGrid Administrative Utilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1306 1.19.22 IceGrid Server Activation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311 1.19.23 IceGrid Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1314 1.20 Freeze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1316 1.20.1 Freeze Evictors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317 1.20.1.1 Freeze Evictor Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1318 1.20.1.2 Background Save Evictor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1323 1.20.1.3 Transactional Evictor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1326 1.20.1.4 Using a Freeze Evictor in the File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1330 1.20.1.4.1 Adding an Evictor to the C++ File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1332 1.20.1.4.2 Adding an Evictor to the Java File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1340 1.20.2 Freeze Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1347 1.20.2.1 Freeze Map Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1348 1.20.2.2 Using a Freeze Map in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1355 1.20.2.3 Using a Freeze Map in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1363 1.20.2.4 Using a Freeze Map in the File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375 1.20.2.4.1 Adding a Freeze Map to the C++ File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1377 1.20.2.4.2 Adding a Freeze Map to the Java File System Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1387 1.20.3 Freeze Catalogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1396 1.20.4 Backing Up Freeze Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1398 1.21 FreezeScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1399 1.21.1 Migrating a Freeze Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1400 1.21.1.1 Automatic Database Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1401 1.21.1.2 Custom Database Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1404 1.21.1.3 FreezeScript Transformation XML Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1408 1.21.1.4 Using transformdb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1414 1.21.2 Inspecting a Freeze Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1420 1.21.2.1 Using dumpdb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1421 1.21.2.2 FreezeScript Inspection XML Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1427 1.21.3 FreezeScript Descriptor Expression Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1432 1.22 IceSSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1435 1.22.1 Using IceSSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1437 1.22.2 Configuring IceSSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1440 1.22.3 Programming IceSSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1449 1.22.3.1 Programming IceSSL in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1450 1.22.3.2 Programming IceSSL in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1455 1.22.3.3 Programming IceSSL in .NET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1459 1.22.4 Advanced IceSSL Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1463 1.22.5 Setting up a Certificate Authority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1470 1.23 Glacier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1474Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.23.1 Common Firewall Traversal Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1475 1.23.2 About Glacier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1476 1.23.3 How Glacier2 Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1477 1.23.4 Getting Started with Glacier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1478 1.23.5 Callbacks through Glacier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1483 1.23.6 Glacier2 Helper Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1486 1.23.7 Securing a Glacier2 Router . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1492 1.23.8 Glacier2 Session Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1498 1.23.9 Dynamic Request Filtering with Glacier2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1501 1.23.10 Glacier2 Request Buffering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1503 1.23.11 How Glacier2 uses Request Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1504 1.23.12 Configuring Glacier2 behind an External Firewall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1506 1.23.13 Advanced Glacier2 Client Configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1507 1.23.14 IceGrid and Glacier2 Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1508 1.24 IceBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1510 1.24.1 Developing IceBox Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1511 1.24.2 Configuring IceBox Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1515 1.24.3 Starting the IceBox Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1518 1.24.4 IceBox Administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1520 1.25 IceStorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1524 1.25.1 IceStorm Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1525 1.25.2 IceStorm Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1527 1.25.3 Using IceStorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1529 1.25.3.1 Implementing an IceStorm Publisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1530 1.25.3.2 Using an IceStorm Publisher Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1534 1.25.3.3 Implementing an IceStorm Subscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1536 1.25.3.4 Publishing to a Specific Subscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1541 1.25.4 Highly Available IceStorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1543 1.25.5 IceStorm Administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1547 1.25.6 Topic Federation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1549 1.25.7 IceStorm Quality of Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1553 1.25.8 IceStorm Delivery Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555 1.25.9 Configuring IceStorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1557 1.26 IcePatch2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1561 1.26.1 Using icepatch2calc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1562 1.26.2 Running the IcePatch2 Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1564 1.26.3 Running the IcePatch2 Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1565 1.26.4 IcePatch2 Object Identities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1567 1.26.5 IcePatch2 Client Utility Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1568 1.27 Property Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1571 1.27.1 Ice Configuration Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1572 1.27.2 Ice Trace Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1573 1.27.3 Ice Warning Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1576 1.27.4 Ice Object Adapter Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1578 1.27.5 Ice Administrative Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1582 1.27.6 Ice Plug-In Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1584 1.27.7 Ice Thread Pool Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1587 1.27.8 Ice Default and Override Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1590 1.27.9 Ice Proxy Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1594 1.27.10 Ice Transport Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1597 1.27.11 Ice Miscellaneous Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1600 1.27.12 IceSSL Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1608 1.27.13 IceBox Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1620 1.27.14 IceBoxAdmin Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1623 1.27.15 IceGrid Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1624 1.27.16 IceGrid Administrative Client Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1639 1.27.17 IceStorm Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1641 1.27.18 Glacier2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1649 1.27.19 Freeze Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1659 1.27.20 IcePatch2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1665 1.28 Windows Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1667 1.28.1 Installing a Windows Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1668 1.28.2 Using the Ice Service Installer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1669 1.28.3 Manually Installing a Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1672 1.28.4 Troubleshooting Windows Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1678 1.29 Binary Distributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1680 1.29.1 Ice Developer Kits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1681 1.29.2 Guidelines for Distributing Ice Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1682 1.30 Deprecated AMI Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1685 1.30.1 Overview of Deprecated AMI Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1686 1.30.2 Deprecated AMI Language Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1690 1.30.3 Advanced Topics for Deprecated AMI Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1697 1.31 IDE Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1704 1.31.1 Visual Studio Add-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1705 1.31.2 Eclipse Plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1708Ice 3.4.2 Documentation Copyright © 2011, ZeroC, Inc. 1.31.3 Xcode Plug-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1710 2. Release Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1712 2.1 New Features in Ice 3.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1713 2.2 Upgrading your Application from Ice 3.4.x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1719 2.3 Upgrading your Application from Ice 3.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1722 2.4 Upgrading your Application from Ice 3.2 or Earlier Releases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1737 2.5 Platform Notes for Ice 3.4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1743 2.6 Known Problems in Ice 3.4.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1745 2.7 Using the Windows Binary Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1747 2.8 Using the Linux RPMs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1756 2.9 Using the Mac OS X Binary Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1761 2.10 Using the Solaris Binary Distributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1764Ice 3.4.2 Documentation 10 Copyright © 2011, ZeroC, Inc. Ice Manual Distributed Programming with Ice The Internet Communications Engine (Ice) is a modern object-oriented toolkit that enables you to build distributed applications with minimal effort. Ice allows you to focus your efforts on your application logic, and it takes care of all interactions with low-level network programming interfaces. With Ice, there is no need to worry about details such as opening network connections, serializing and deserializing data for network transmission, or retrying failed connection attempts. The main design goals of Ice are: Provide an object-oriented middleware platform suitable for use in heterogeneous environments. Provide a full set of features that support development of realistic distributed applications for a wide variety of domains. Avoid unnecessary complexity, making the platform easy to learn and to use. Provide an implementation that is efficient in network bandwidth, memory use, and CPU overhead. Provide an implementation that has built-in security, making it suitable for use over insecure public networks. In simpler terms, the Ice design goals could be stated as "Let's build a powerful middleware platform that makes the developer's life easier." The acronym "Ice" is pronounced as a single syllable, like the word for frozen water. Getting Help with Ice If you have a question and you cannot find an answer in this manual, you can visit our to see if another developer hasdeveloper forums encountered the same issue. If you still need help, feel free to post your question on the forum, which ZeroC's developers monitor regularly. Note, however, that we can provide only limited free support in our forums. For guaranteed response and problem resolution times, we highly recommend purchasing .commercial support Feedback about the Manual We would very much like to hear from you in case you find any bugs (however minor) in this manual. We also would like to hear your opinion on the contents, and any suggestions as to how it might be improved. You can contact us via e-mail at .icebook@zeroc.com Legal Notices Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book and ZeroC was aware of the trademark claim, the designations have been printed in initial caps or all caps. The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. License This manual is provided under one of two licenses, whichever you prefer: Creative Commons Attribution-No Derivative Works 3.0 Unported License. This license does not permit you to make modifications. Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. This license permits you to make modifications. If you distribute this manual under this license, you must prominently include the following text:Ice 3.4.2 Documentation 11 Copyright © 2011, ZeroC, Inc. This document is derived from ZeroC's , Copyright © ZeroC, Inc. 2003-2011.Ice Manual You can find the latest version of this document at: http://doc.zeroc.com/display/Ice/Ice+Manual Copyright Copyright © 2003-2011 by ZeroC, Inc. mailto:info@zeroc.com http://www.zeroc.comIce 3.4.2 Documentation 12 Copyright © 2011, ZeroC, Inc. Ice Overview The following topics provide a high-level overview of Ice: Ice Architecture introduces fundamental concepts and terminology, and outlines how Slice definitions, language mappings, and the Ice run time and protocol work in concert to create clients and servers. Ice Services briefly presents the object services provided by Ice. Architectural Benefits of Ice outlines the benefits that result from the Ice architecture. Topics Ice Architecture Ice Services Architectural Benefits of IceIce 3.4.2 Documentation 13 Copyright © 2011, ZeroC, Inc. Ice Architecture Ice is an object-oriented middleware platform. Fundamentally, this means that Ice provides tools, APIs, and library support for building object-oriented client-server applications. Ice applications are suitable for use in heterogeneous environments: client and server can be written in different programming languages, can run on different operating systems and machine architectures, and can communicate using a variety of networking technologies. The source code for these applications is portable regardless of the deployment environment. Topics: Terminology Slice (Specification Language for Ice) Language Mappings Client and Server Structure Overview of the Ice Protocol See Also Ice Services Architectural Benefits of IceIce 3.4.2 Documentation 14 Copyright © 2011, ZeroC, Inc. Terminology Every computing technology creates its own vocabulary as it evolves. Ice is no exception. However, the amount of new jargon used by Ice is minimal. Rather than inventing new terms, we have used existing terminology as much as possible. If you have used another middleware technology in the past, you will be familiar with much of what follows. (However, we suggest you at least skim the material because a few terms used by Ice differ from the corresponding terms used by other middleware.)do On this page: Clients and Servers Ice Objects Proxies Stringified Proxies Direct Proxies Indirect Proxies Direct Versus Indirect Binding Fixed Proxies Routed Proxies Replication Replica Groups Servants At-Most-Once Semantics Synchronous Method Invocation Asynchronous Method Invocation Asynchronous Method Dispatch Oneway Method Invocation Batched Oneway Method Invocation Datagram Invocations Batched Datagram Invocations Run-Time Exceptions User Exceptions Properties Clients and Servers The terms and are not firm designations for particular parts of an application; rather, they denote roles that are taken by parts ofclient server an application for the duration of a request: Clients are active entities. They issue requests for service to servers. Servers are passive entities. They provide services in response to client requests. Frequently, servers are not "pure" servers, in the sense that they never issue requests and only respond to requests. Instead, servers often act as a server on behalf of some client but, in turn, act as a client to another server in order to satisfy their client's request. Similarly, clients often are not "pure" clients, in the sense that they only request service from an object. Instead, clients are frequently client-server hybrids. For example, a client might start a long-running operation on a server; as part of starting the operation, the client can provide a to the server that is used by the server to notify the client when the operation is complete. In that case, the clientcallback object acts as a client when it starts the operation, and as a server when it is notified that the operation is complete. Such role reversal is common in many systems, so, frequently, client-server systems could be more accurately described as peer-to-peer systems. Ice Objects An is a conceptual entity, or abstraction. An Ice object can be characterized by the following points:Ice object An Ice object is an entity in the local or a remote address space that can respond to client requests. A single Ice object can be instantiated in a single server or, redundantly, in multiple servers. If an object has multiple simultaneous instantiations, it is still a single Ice object. Each Ice object has one or more . An interface is a collection of named that are supported by an object. Clientsinterfaces operations issue requests by invoking operations. An operation has zero or more as well as a . Parameters and return values have a specific . Parametersparameters return value type are named and have a direction: in-parameters are initialized by the client and passed to the server; out-parameters are initialized by the server and passed to the client. (The return value is simply a special out-parameter.) An Ice object has a distinguished interface, known as its . In addition, an Ice object can provide zero or more alternatemain interface interfaces, known as . Clients can select among the facets of an object to choose the interface they want to work with.facets Each Ice object has a unique . An object's identity is an identifying value that distinguishes the object from all otherobject identityIce 3.4.2 Documentation 15 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. objects. The Ice object model assumes that object identities are globally unique, that is, no two objects within an Ice communication domain can have the same object identity. In practice, you need not use object identities that are globally unique, such as , only identities that do not clash with anyUUIDs other identity within your domain of interest. However, there are architectural advantages to using globally unique identifiers, which we explore in our discussion of .object life cycle Proxies For a client to be able to contact an Ice object, the client must hold a for the Ice object. A is an artifact that is local to the client'sproxy proxy address space; it represents the (possibly remote) Ice object for the client. A proxy acts as the local ambassador for an Ice object: when the client invokes an operation on the proxy, the Ice run time: Locates the Ice object Activates the Ice object's server if it is not running Activates the Ice object within the server Transmits any in-parameters to the Ice object Waits for the operation to complete Returns any out-parameters and the return value to the client (or throws an exception in case of an error) A proxy encapsulates all the necessary information for this sequence of steps to take place. In particular, a proxy contains: Addressing information that allows the client-side run time to contact the correct server An object identity that identifies which particular object in the server is the target of a request An optional facet identifier that determines which particular facet of an object the proxy refers to Stringified Proxies The information in a proxy can be expressed as a string. For example, the string: SimplePrinter:default -p 10000 is a human-readable representation of a proxy. The Ice run time provides API calls that allow you to convert a proxy to its stringified form and vice versa. This is useful, for example, to store proxies in database tables or text files. Provided that a client knows the identity of an Ice object and its addressing information, it can create a proxy "out of thin air" by supplying that information. In other words, no part of the information inside a proxy is considered opaque; a client needs to know only an object's identity, addressing information, and (to be able to invoke an operation) the object's type in order to contact the object. Direct Proxies A is a proxy that embeds an object's identity, together with the address at which its server runs. The address is completelydirect proxy specified by: a protocol identifier (such TCP/IP or UDP) a protocol-specific address (such as a host name and port number) To contact the object denoted by a direct proxy, the Ice run time uses the addressing information in the proxy to contact the server; the identity of the object is sent to the server with each request made by the client. Indirect Proxies An has two forms. It may provide only an object's identity, or it may specify an identity together with an object adapterindirect proxy identifier. An object that is accessible using only its identity is called a well-known object. For example, the string: SimplePrinter is a valid proxy for a well-known object with the identity .SimplePrinter An indirect proxy that includes an object adapter identifier has the stringified form SimplePrinter@PrinterAdapterIce 3.4.2 Documentation 16 Copyright © 2011, ZeroC, Inc. Any object of the object adapter can be accessed using such a proxy, regardless of whether that object is also a well-known object. Notice that an indirect proxy contains no addressing information. To determine the correct server, the client-side run time passes the proxy information to a . In turn, the location service uses the object identity or the object adapter identifier as the key in a lookuplocation service table that contains the address of the server and returns the current server address to the client. The client-side run time now knows how to contact the server and dispatches the client request as usual. The entire process is similar to the mapping from Internet domain names to IP address by the Domain Name Service (DNS): when we use a domain name, such as , to look up a web page, the host name is first resolved to an IP address behind the scenes and,www.zeroc.com once the correct IP address is known, the IP address is used to connect to the server. With Ice, the mapping is from an object identity or object adapter identifier to a protocol-address pair, but otherwise very similar. The client-side run time knows how to contact the location service via configuration (just as web browsers know which DNS server to use via configuration). Direct Versus Indirect Binding The process of resolving the information in a proxy to protocol-address pair is known as . Not surprisingly, is used forbinding direct binding direct proxies, and is used for indirect proxies.indirect binding The main advantage of indirect binding is that it allows us to move servers around (that is, change their address) without invalidating existing proxies that are held by clients. In other words, direct proxies avoid the extra lookup to locate the server but no longer work if a server is moved to a different machine. On the other hand, indirect proxies continue to work even if we move (or ) a server.migrate Fixed Proxies A is a proxy that is bound to a particular connection: instead of containing addressing information or an adapter name, the proxyfixed proxy contains a connection handle. The connection handle stays valid only for as long as the connection stays open so, once the connection is closed, the proxy no longer works (and will never work again). Fixed proxies cannot be marshaled, that is, they cannot be passed as parameters on operation invocations. Fixed proxies are used to allow , so a server can make callbacks to a clientbidirectional communication without having to open a new connection. Routed Proxies A is a proxy that forwards all invocations to a specific target object, instead of sending invocations directly to the actual target.routed proxy Routed proxies are useful for implementing services such as , which enables clients to communicate with servers that are behind aGlacier2 firewall. Replication In Ice, involves making object adapters (and their objects) available at multiple addresses. The goal of replication is usually toreplication provide redundancy by running the same server on several computers. If one of the computers should happen to fail, a server still remains available on the others. The use of replication implies that applications are designed for it. In particular, it means a client can access an object via one address and obtain the same result as from any other address. Either these objects are stateless, or their implementations are designed to synchronize with a database (or each other) in order to maintain a consistent view of each object's state. Ice supports a limited form of replication when a proxy specifies multiple addresses for an object. The Ice run time selects one of the addresses at random for its and tries all of them in the case of a failure. For example, consider this proxy:initial connection attempt SimplePrinter:tcp -h server1 -p 10001:tcp -h server2 -p 10002 The proxy states that the object with identity is available using TCP at two addresses, one on the host andSimplePrinter server1 another on the host . The burden falls to users or system administrators to ensure that the servers are actually running on theseserver2 computers at the specified ports. Replica Groups In addition to the proxy-based replication described above, Ice supports a more useful form of replication known as thatreplica groups requires the use of a .location service A replica group has a unique identifier and consists of any number of object adapters. An object adapter may be a member of at most one replica group; such an adapter is considered to be a .replicated object adapter After a replica group has been established, its identifier can be used in an indirect proxy in place of an adapter identifier. For example, a replica group identified as can be used in a proxy as shown below:PrinterAdaptersIce 3.4.2 Documentation 17 Copyright © 2011, ZeroC, Inc. SimplePrinter@PrinterAdapters The replica group is treated by the location service as a "virtual object adapter." The behavior of the location service when resolving an indirect proxy containing a replica group id is an implementation detail. For example, the location service could decide to return the addresses of all object adapters in the group, in which case the client's Ice run time would select one of the addresses at random using the limited form of replication discussed earlier. Another possibility is for the location service to return only one address, which it decided upon using some heuristic. Regardless of the way in which a location service resolves a replica group, the key benefit is indirection: the location service as a middleman can add more intelligence to the binding process. Servants As we mentioned, an is a conceptual entity that has a type, identity, and addressing information. However, client requestsIce Object ultimately must end up with a concrete server-side processing entity that can provide the behavior for an operation invocation. To put this differently, a client request must ultimately end up executing code inside the server, with that code written in a specific programming language and executing on a specific processor. The server-side artifact that provides behavior for operation invocations is known as a . A servant provides substance for (or servant ) one or more Ice objects. In practice, a servant is simply an instance of a class that is written by the server developer and that isincarnates registered with the server-side run time as the servant for one or more Ice objects. Methods on the class correspond to the operations on the Ice object's interface and provide the behavior for the operations. A single servant can incarnate a single Ice object at a time or several Ice objects simultaneously. If the former, the identity of the Ice object incarnated by the servant is implicit in the servant. If the latter, the servant is provided the identity of the Ice object with each request, so it can decide which object to incarnate for the duration of the request. Conversely, a single Ice object can have multiple servants. For example, we might choose to create a proxy for an Ice object with two different addresses for different machines. In that case, we will have two servers, with each server containing a servant for the same Ice object. When a client invokes an operation on such an Ice object, the client-side run time sends the request to exactly one server. In other words, multiple servants for a single Ice object allow you to build redundant systems: the client-side run time attempts to send the request to one server and, if that attempt fails, sends the request to the second server. An error is reported back to the client-side application code only if that second attempt also fails. At-Most-Once Semantics Ice requests have semantics: the Ice run time does its best to deliver a request to the correct destination and, depending onat-most-once the exact circumstances, may retry a failed request. Ice guarantees that it will either deliver the request, or, if it cannot deliver the request, inform the client with an appropriate exception; under no circumstances is a request delivered twice, that is, retries are attempted only if it is known that a previous attempt definitely failed. One exception to this rule are datagram invocations over UDP transports. For these, duplicated UDP packets can lead to a violation of at-most-once semantics. At-most-once semantics are important because they guarantee that operations that are not can be used safely. An idempotentidempotent operation is an operation that, if executed twice, has the same effect as if executed once. For example, is an idempotent operation:x = 1; if we execute the operation twice, the end result is the same as if we had executed it once. On the other hand, is not idempotent: if wex++; execute the operation twice, the end result is not the same as if we had executed it once. Without at-most-once semantics, we can build distributed systems that are more robust in the presence of network failures. However, realistic systems require non-idempotent operations, so at-most-once semantics are a necessity, even though they make the system less robust in the presence of network failures. Ice permits you to mark individual operations as idempotent. For such operations, the Ice run time uses a more aggressive error recovery mechanism than for non-idempotent operations. Synchronous Method Invocation By default, the request dispatch model used by Ice is a synchronous remote procedure call: an operation invocation behaves like a local procedure call, that is, the client thread is suspended for the duration of the call and resumes when the call completes (and all its results are available). Asynchronous Method Invocation Ice also supports : clients can invoke operations , that is, the client uses a proxy asasynchronous method invocation (AMI) asynchronouslyIce 3.4.2 Documentation 18 Copyright © 2011, ZeroC, Inc. usual to invoke an operation but, in addition to passing the normal parameters, also passes a and the client invocationcallback object returns immediately. Once the operation completes, the client-side run time invokes a method on the callback object passed initially, passing the results of the operation to the callback object (or, in case of failure, passing exception information). The server cannot distinguish an asynchronous invocation from a synchronous one — either way, the server simply sees that a client has invoked an operation on an object. Asynchronous Method Dispatch Asynchronous method dispatch (AMD) is the server-side equivalent of AMI. For synchronous dispatch (the default), the server-side run time up-calls into the application code in the server in response to an operation invocation. While the operation is executing (or sleeping, for example, because it is waiting for data), a thread of execution is tied up in the server; that thread is released only when the operation completes. With asynchronous method dispatch, the server-side application code is informed of the arrival of an operation invocation. However, instead of being forced to process the request immediately, the server-side application can choose to delay processing of the request and, in doing so, releases the execution thread for the request. The server-side application code is now free to do whatever it likes. Eventually, once the results of the operation are available, the server-side application code makes an API call to inform the server-side Ice run time that a request that was dispatched previously is now complete; at that point, the results of the operation are returned to the client. Asynchronous method dispatch is useful if, for example, a server offers operations that block clients for an extended period of time. For example, the server may have an object with a operation that returns data from an external, asynchronous data source and that blocksget clients until the data becomes available. With synchronous dispatch, each client waiting for data to arrive ties up an execution thread in the server. Clearly, this approach does not scale beyond a few dozen clients. With asynchronous dispatch, hundreds or thousands of clients can be blocked in the same operation invocation without tying up any threads in the server. Another way to use asynchronous method dispatch is to complete an operation, so the results of the operation are returned to the client, but to keep the execution thread of the operation beyond the duration of the operation invocation. This allows you to continue processing after results have been returned to the client, for example, to perform cleanup or write updates to persistent storage. Synchronous and asynchronous method dispatch are transparent to the client, that is, the client cannot tell whether a server chose to process a request synchronously or asynchronously. Oneway Method Invocation Clients can invoke an operation as a operation. A oneway invocation has "best effort" semantics. For a oneway invocation, theoneway client-side run time hands the invocation to the local transport, and the invocation completes on the client side as soon as the local transport has buffered the invocation. The actual invocation is then sent asynchronously by the operating system. The server does not reply to oneway invocations, that is, traffic flows only from client to server, but not vice versa. Oneway invocations are unreliable. For example, the target object may not exist, in which case the invocation is simply lost. Similarly, the operation may be dispatched to a servant in the server, but the operation may fail (for example, because parameter values are invalid); if so, the client receives no notification that something has gone wrong. Oneway invocations are possible only on that do not have a return value, do not have out-parameters, and do not throw useroperations exceptions. To the application code on the server-side, oneway invocations are transparent, that is, there is no way to distinguish a twoway invocation from a oneway invocation. Oneway invocations are available only if the target object offers a stream-oriented transport, such as TCP/IP or SSL. Note that, even though oneway operations are sent over a stream-oriented transport, they may be processed out of order in the server. This can happen because each invocation may be dispatched in its own thread: even though the invocations are in the order in which theinitiated invocations arrive at the server, this does not mean that they will be in that order — the vagaries of thread scheduling can result inprocessed a oneway invocation completing before other oneway invocations that were received earlier. Batched Oneway Method Invocation Each oneway invocation sends a separate message to the server. For a series of short messages, the overhead of doing so is considerable: the client- and server-side run time each must switch between user mode and kernel mode for each message and, at the networking level, each message incurs the overheads of flow-control and acknowledgement. Batched oneway invocations allow you to send a series of oneway invocations as a single message: every time you invoke a batched oneway operation, the invocation is buffered in the client-side run time. Once you have accumulated all the oneway invocations you want to send, you make a separate API call to send all the invocations at once. The client-side run time then sends all of the buffered invocations in a single message, and the server receives all of the invocations in a single message. This avoids the overhead of repeatedly trapping into the kernel for both client and server, and is much easier on the network between them because one large message can be transmitted more efficiently than many small ones.Ice 3.4.2 Documentation 19 Copyright © 2011, ZeroC, Inc. The individual invocations in a batched oneway message are dispatched by a single thread in the order in which they were placed into the batch. This guarantees that the individual operations in a batched oneway message are processed in order in the server. Batched oneway invocations are particularly useful for messaging services, such as , and for fine-grained interfaces that offer IceStorm set operations for small attributes. Datagram Invocations Datagram invocations have "best effort" semantics similar to oneway invocations. However, datagram invocations require the object to offer UDP as a transport (whereas oneway invocations require TCP/IP). Like a oneway invocation, a datagram invocation can be made only if the operation does not have a return value, out-parameters, or user exceptions. A datagram invocation uses UDP to invoke the operation. The operation returns as soon as the local UDP stack has accepted the message; the actual operation invocation is sent asynchronously by the network stack behind the scenes. Datagrams, like oneway invocations, are unreliable: the target object may not exist in the server, the server may not be running, or the operation may be invoked in the server but fail due to invalid parameters sent by the client. As for oneway invocations, the client receives no notification of such errors. However, unlike oneway invocations, datagram invocations have a number of additional error scenarios: Individual invocations may simply be lost in the network. This is due to the unreliable delivery of UDP packets. For example, if you invoke three operations in sequence, the middle invocation may be lost. (The same thing cannot happen for oneway invocations — because they are delivered over a connection-oriented transport, individual invocations cannot be lost.) Individual invocations may arrive out of order. Again, this is due to the nature of UDP datagrams. Because each invocation is sent as a separate datagram, and individual datagrams can take different paths through the network, it can happen that invocations arrive in an order that differs from the order in which they were sent. Datagram invocations are well suited for small messages on LANs, where the likelihood of loss is small. They are also suited to situations in which low latency is more important than reliability, such as for fast, interactive internet applications. Finally, datagram invocations can be used to multicast messages to multiple servers simultaneously. Batched Datagram Invocations As for batched oneway invocations, allow you to accumulate a number of invocations in a buffer and thenbatched datagram invocations send the entire buffer as a single datagram by making an API call to flush the buffer. Batched datagrams reduce the overhead of repeated system calls and allow the underlying network to operate more efficiently. However, batched datagram invocations are useful only for batched messages whose total size does not substantially exceed the PDU limit of the network: if the size of a batched datagram gets too large, UDP fragmentation makes it more likely that one or more fragments are lost, which results in the loss of the entire batched message. However, you are guaranteed that either all invocations in a batch will be delivered, or none will be delivered. It is impossible for individual invocations within a batch to be lost. Batched datagrams use a single thread in the server to dispatch the individual invocations in a batch. This guarantees that the invocations are made in the order in which they were queued — invocations cannot appear to be reordered in the server. Run-Time Exceptions Any operation invocation can raise a . Run-time exceptions are pre-defined by the Ice run time and cover common errorrun-time exception conditions, such as connection failure, connection timeout, or resource allocation failure. Run-time exceptions are presented to the application as native exceptions and so integrate neatly with the native exception handling capabilities of languages that support exception handling. User Exceptions A server indicates application-specific error conditions by raising to clients. User exceptions can carry an arbitrary amount ofuser exceptions complex data and can be arranged into inheritance hierarchies, which makes it easy for clients to handle categories of errors generically, by catching an exception that is further up the inheritance hierarchy. Like run-time exceptions, user exceptions map to native exceptions. Properties Much of the Ice run time is configurable via . Properties are name-value pairs, such as .properties Ice.Default.Protocol=tcp Properties are typically stored in text files and parsed by the Ice run time to configure various options, such as the thread pool size, the level of tracing, and various other configuration parameters.Ice 3.4.2 Documentation 20 Copyright © 2011, ZeroC, Inc. See Also The Slice Language Proxies Locators Object Life Cycle Bidirectional Connections Glacier2 IceStormIce 3.4.2 Documentation 21 Copyright © 2011, ZeroC, Inc. Slice (Specification Language for Ice) Each has an interface with a number of operations. Interfaces, operations, and the types of data that are exchanged betweenIce object client and server are defined using the . Slice allows you to define the client-server contract in a way that is independent of aSlice language specific programming language, such as C++, Java, or C#. The Slice definitions are compiled by a compiler into an API for a specific programming language, that is, the part of the API that is specific to the interfaces and types you have defined consists of generated code. See Also The Slice LanguageIce 3.4.2 Documentation 22 Copyright © 2011, ZeroC, Inc. Language Mappings The rules that govern how each Slice construct is translated into a specific programming language are known as . Forlanguage mappings example, for the , a Slice sequence appears as an STL vector, whereas, for the , a Slice sequence appears as aC++ mapping Java mapping Java array. In order to determine what the API for a specific Slice construct looks like, you only need the Slice definition and knowledge of the language mapping rules. The rules are simple and regular enough to make it unnecessary to read the generated code to work out how to use the generated API. Of course, you are free to peruse the generated code. However, as a rule, that is inefficient because the generated code is not necessarily suitable for human consumption. We recommend that you familiarize yourself with the language mapping rules; that way, you can mostly ignore the generated code and need to refer to it only when you are interested in some specific detail. Currently, Ice provides language mappings for C++, Java, C#, Python, Objective-C, and, for the client side, PHP and Ruby. See Also C++ Mapping Java Mapping C# Mapping Objective-C Mapping Python Mapping Ruby Mapping PHP MappingIce 3.4.2 Documentation 23 Copyright © 2011, ZeroC, Inc. Client and Server Structure Ice clients and servers have the logical internal structure: Ice Client and Server Structure Both client and server consist of a mixture of application code, library code, and code generated from Slice definitions: The Ice core contains the client- and server-side run-time support for remote communication. Much of this code is concerned with the details of networking, threading, byte ordering, and many other networking-related issues that we want to keep away from application code. The Ice core is provided as a number of libraries that client and server use. The generic part of the Ice core (that is, the part that is independent of the specific types you have defined in Slice) is accessed through the Ice API. You use the Ice API to take care of administrative chores, such as initializing and finalizing the Ice run time. The Ice API is identical for clients and servers (although servers use a larger part of the API than clients). The proxy code is generated from your Slice definitions and, therefore, specific to the types of objects and data you have defined in Slice. The proxy code has two major functions: It provides a down-call interface for the client. Calling a function in the generated proxy API ultimately ends up sending an RPC message to the server that invokes a corresponding function on the target object. It provides and code. Marshaling is the process of serializing a complex data structure, such as amarshaling unmarshaling sequence or a dictionary, for transmission on the wire. The marshaling code converts data into a form that is standardized for transmission and independent of the endian-ness and padding rules of the local machine. Unmarshaling is the reverse of marshaling, that is, deserializing data that arrives over the network and reconstructing a local representation of the data in types that are appropriate for the programming language in use. The skeleton code is also generated from your Slice definition and, therefore, specific to the types of objects and data you have defined in Slice. The skeleton code is the server-side equivalent of the client-side proxy code: it provides an up-call interface that permits the Ice run time to transfer the thread of control to the application code you write. The skeleton also contains marshaling and unmarshaling code, so the server can receive parameters sent by the client, and return parameters and exceptions to the client. The object adapter is a part of the Ice API that is specific to the server side: only servers use object adapters. An object adapter has several functions: The object adapter maps incoming requests from clients to specific methods on programming-language objects. In other words, the object adapter tracks which servants with what object identity are in memory. The object adapter is associated with one or more transport endpoints. If more than one transport endpoint is associated with an adapter, the servants incarnating objects within the adapter can be reached via multiple transports. For example, you can associate both a TCP/IP and a UDP endpoint with an adapter, to provide alternate quality-of-service and performance characteristics. The object adapter is responsible for the creation of proxies that can be passed to clients. The object adapter knows about the type, identity, and transport details of each of its objects and embeds the correct details when the server-sideIce 3.4.2 Documentation 24 Copyright © 2011, ZeroC, Inc. application code requests the creation of a proxy. Note that, as far as the process view is concerned, there are only two processes involved: the client and the server. All the run time support for distributed communication is provided by the Ice libraries and the code that is generated from Slice definitions. (For indirect proxies, a third process, , is required to resolve proxies to transport endpoints.)IceGrid See Also Hello World Application IceGridIce 3.4.2 Documentation 25 Copyright © 2011, ZeroC, Inc. Overview of the Ice Protocol Ice provides an that can use either TCP/IP or UDP as an underlying transport. In addition, Ice also allows you to use as aRPC protocol SSL transport, so all communication between client and server is encrypted. The Ice protocol defines: a number of message types, such as request and reply message types, a protocol state machine that determines in what sequence different message types are exchanged by client and server, together with the associated connection establishment and tear-down semantics for TCP/IP, encoding rules that determine how each type of data is represented on the wire, a header for each message type that contains details such as the message type, the message size, and the protocol and encoding version in use. Ice also supports compression on the wire: by setting a configuration parameter, you can arrange for all network traffic to be compressed to conserve bandwidth. This is useful if your application exchanges large amounts of data between client and server. The Ice protocol is suitable for building highly-efficient event forwarding mechanisms because it permits forwarding of a message without knowledge of the details of the information inside a message. This means that messaging switches need not do any unmarshaling and remarshaling of messages — they can forward a message by simply treating it as an opaque buffer of bytes. The Ice protocol also supports : if a server wants to send a message to a callback object provided by the client, thebidirectional operation callback can be made over the connection that was originally created by the client. This feature is especially important when the client is behind a firewall that permits outgoing connections, but not incoming connections. See Also The Ice Protocol IceSSL Bidirectional ConnectionsIce 3.4.2 Documentation 26 Copyright © 2011, ZeroC, Inc. Ice Services The Ice core provides a sophisticated client-server platform for distributed application development. However, realistic applications usually require more than just a remoting capability: typically, you also need a way to start servers on demand, distribute proxies to clients, distribute asynchronous events, configure your application, distribute patches for an application, and so on. Ice ships with a number of services that provide these and other features. The services are implemented as Ice servers to which your application acts as a client. None of the services use Ice-internal features that are hidden from application developers so, in theory, you could develop equivalent services yourself. However, having these services available as part of the platform allows you to focus on application development instead of having to build a lot of infrastructure first. Moreover, building such services is not a trivial effort, so it pays to know what is available and use it instead of reinventing your own wheel. On this page: Freeze and FreezeScript IceGrid Service IceBox Server IceStorm IcePatch2 Glacier2 Freeze and FreezeScript Ice has a built-in object persistence service, known as . Freeze makes it easy to store object state in a database: you define the stateFreeze stored by your objects in Slice, and the Freeze compiler generates code that stores and retrieves object state to and from a database. Freeze uses as its database.Berkeley DB Ice also offers a tool set collectively called that makes it easier to maintain databases and to migrate the contents of existingFreezeScript databases to a new schema if the type definitions of objects change. IceGrid Service IceGrid is an implementation of an Ice that resolves the symbolic information in an indirect proxy to a protocol-address pairlocation service for indirect binding. A location service is only the beginning of IceGrid's capabilities. IceGrid: allows you to register servers for automatic start-up: instead of requiring a server to be running at the time a client issues a request, IceGrid starts servers on demand, when the first client request arrives. provides tools that make it easy to configure complex applications containing several servers. supports replication and load-balancing. automates the distribution and patching of server executables and dependent files. provides a simple query service that allows clients to obtain proxies for objects they are interested in. IceBox Server IceBox is a simple application server that can orchestrate the starting and stopping of a number of application components. Application components can be deployed as a dynamic library instead of as a process. This reduces overall system load, for example, by allowing you to run several application components in a single Java virtual machine instead of having multiple processes, each with its own virtual machine. IceStorm IceStorm is a publish-subscribe service that decouples clients and servers. Fundamentally, IceStorm acts as a distribution switch for events. Publishers send events to the service, which, in turn, passes the events to subscribers. In this way, a single event published by a publisher can be sent to multiple subscribers. Events are categorized by topic, and subscribers specify the topics they are interested in. Only events that match a subscriber's topic are sent to that subscriber. The service permits selection of a number of quality-of-service criteria to allow applications to choose the appropriate trade-off between reliability and performance. IceStorm is particularly useful if you have a need to distribute information to large numbers of application components. (A typical example is a stock ticker application with a large number of subscribers.) IceStorm decouples the publishers of information from subscribers and takes care of the redistribution of the published events. In addition, IceStorm can be run as a service, that is, multiple instances of thefederated service can be run on different machines to spread the processing load over a number of CPUs.Ice 3.4.2 Documentation 27 Copyright © 2011, ZeroC, Inc. IcePatch2 IcePatch2 is a software patching service. It allows you to easily distribute software updates to clients. Clients simply connect to the IcePatch2 server and request updates for a particular application. The service automatically checks the version of the client's software and downloads any updated application components in a compressed format to conserve bandwidth. Software patches can be secured using the Glacier2 service, so only authorized clients can download software updates. IcePatch2 supersedes IcePatch, which was a previous version of this service. Glacier2 Glacier2 is the Ice firewall traversal service: it allows clients and servers to securely communicate through a firewall without compromising security. Client-server traffic is SSL-encrypted using public key certificates and is bidirectional. Glacier2 offers support for mutual authentication as well as secure session management. Glacier2 supersedes Glacier, which was a previous version of this service See Also IceGrid Freeze FreezeScript Glacier2 IceBox IceStorm IcePatch2Ice 3.4.2 Documentation 28 Copyright © 2011, ZeroC, Inc. Architectural Benefits of Ice The Ice architecture provides a number of benefits to application developers: Object-oriented semantics Ice fully preserves the object-oriented paradigm "across the wire." All operation invocations use late binding, so the implementation of an operation is chosen depending on the actual run-time (not static) type of an object. Support for synchronous and asynchronous messaging Ice provides both synchronous and asynchronous operation invocation and dispatch, as well as publish-subscribe messaging via IceStorm. This allows you to choose a communication model according to the needs of your application instead of having to shoe-horn the application to fit a single model. Support for multiple interfaces With facets, objects can provide multiple, unrelated interfaces while retaining a single object identity across these interfaces. This provides great flexibility, particularly as an application evolves but needs to remain compatible with older, already deployed clients. Machine independence Clients and servers are shielded form idiosyncrasies of the underlying machine architecture. Issues such as byte ordering and padding are hidden from application code. Language independence Client and server can be developed independently and in different programming languages. The Slice definition used by both client and server establishes the interface contract between them and is the only thing they need to agree on. Implementation independence Clients are unaware of how servers implement their objects. This means that the implementation of a server can be changed after clients are deployed, for example, to use a different persistence mechanism or even a different programming language. Operating system independence The Ice APIs are fully portable, so the same source code compiles and runs under both Windows and Unix. Threading support The Ice run time is fully threaded and APIs are thread-safe. No effort (beyond synchronizing access to shared data) is required on part of the application developer to develop threaded, high-performance clients and servers. Transport independence Ice currently offers both TCP/IP and UDP as transport protocols. Neither client nor server code are aware of the underlying transport. (The desired transport can be chosen by a configuration parameter.) Location and server transparency The Ice run time takes care of locating objects and managing the underlying transport mechanism, such as opening and closing connections. Interactions between client and server appear connection-less. Via IceGrid, you can arrange for servers to be started on demand if they are not running at the time a client invokes an operation. Servers can be migrated to different physical addresses without breaking proxies held by clients, and clients are completely unaware how object implementations are distributed over server processes. Security Communications between client and server can be fully secured with strong encryption over SSL, so applications can use unsecured public networks to communicate securely. Via Glacier2, you can implement secure forwarding of requests through a firewall, with full support for callbacks. Built-in persistence With Freeze, creating persistent object implementations becomes trivial. Ice comes with built-in support for , which is aBerkeley DB high-performance database. Source code availability The source code for Ice is available. While it is not necessary to have access to the source code to use the platform, it allows you to see how things are implemented or port the code to a new operating system. Overall, Ice provides a state-of-the art development and deployment environment for distributed computing that is more complete than any other platform we are aware of. See Also Ice Architecture Ice ServicesIce 3.4.2 Documentation 29 Copyright © 2011, ZeroC, Inc.Ice 3.4.2 Documentation 30 Copyright © 2011, ZeroC, Inc. 1. 2. 3. Hello World Application This section presents a very simple (but complete) client and server. Writing an Ice application involves the following steps: Write a Slice definition and compile it. Write a server and compile it. Write a client and compile it. If someone else has written the server already and you are only writing a client, you do not need to write the Slice definition, only compile it (and, obviously, you do not need to write the server in that case). The application described here enables remote printing: a client sends the text to be printed to a server, which in turn sends that text to a printer. For simplicity (and because we do not want to concern ourselves with the idiosyncrasies of print spoolers for various platforms), our printer will simply print to a terminal instead of a real printer. This is no great loss: the purpose of the exercise is to show how a client can communicate with a server; once the thread of control has reached the server application code, that code can of course do anything it likes (including sending the text to a real printer). How to do this is independent of Ice and therefore not relevant here. Much of the detail of the source code will remain unexplained for now. The intent is to show you how to get started and give you a feel for what the development environment looks like; we will provide all the detail throughout the remainder of this manual. Topics Writing a Slice Definition Writing an Ice Application with C++ Writing an Ice Application with Java Writing an Ice Application with C-Sharp Writing an Ice Application with Visual Basic Writing an Ice Application with Objective-C Writing an Ice Application with Python Writing an Ice Application with Ruby Writing an Ice Application with PHPIce 3.4.2 Documentation 31 Copyright © 2011, ZeroC, Inc. Writing a Slice Definition The first step in writing any Ice application is to write a definition containing the interfaces that are used by the application. For ourSlice minimal printing application, we write the following Slice definition: Slice module Demo { interface Printer { void printString(string s); }; }; We save this text in a file called .Printer.ice Our Slice definitions consist of the module containing a single interface called . For now, the interface is very simple andDemo Printer provides only a single operation, called . The operation accepts a string as its sole input parameter; the text ofprintString printString that string is what appears on the (possibly remote) printer. See Also The Slice LanguageIce 3.4.2 Documentation 32 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with C++ This page shows how to create an Ice application with C++. On this page: Compiling a Slice Definition for C++ Writing and Compiling a Server in C++ Writing and Compiling a Client in C++ Running Client and Server in C++ Compiling a Slice Definition for C++ The first step in creating our C++ application is to compile our to generate C++ proxies and skeletons. Under Unix, you canSlice definition compile the definition as follows: $ slice2cpp Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The compiler produces two C++ source files from this definition, and .slice2cpp Printer.h Printer.cpp Printer.h The header file contains C++ type definitions that correspond to the Slice definitions for our interface. ThisPrinter.h Printer header file must be included in both the client and the server source code. Printer.cpp The file contains the source code for our interface. The generated source contains type-specific run-timePrinter.cpp Printer support for both clients and servers. For example, it contains code that marshals parameter data (the string passed to the operation) on the client side and unmarshals that data on the server side.printString The file must be compiled and linked into both client and server.Printer.cpp Writing and Compiling a Server in C++ The source code for the server takes only a few lines and is shown in full here:Ice 3.4.2 Documentation 33 Copyright © 2011, ZeroC, Inc. C++ #include #include using namespace std; using namespace Demo; class PrinterI : public Printer { public: virtual void printString(const string& s, const Ice::Current&); }; void PrinterI:: printString(const string& s, const Ice::Current&) { cout << s << endl; } int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { ic = Ice::initialize(argc, argv); Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice::ObjectPtr object = new PrinterI; adapter->add(object, ic->stringToIdentity("SimplePrinter")); adapter->activate(); ic->waitForShutdown(); } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) { try { ic->destroy(); } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } } return status; } There appears to be a lot of code here for something as simple as a server that just prints a string. Do not be concerned by this: most of the preceding code is boiler plate that never changes. For this very simple server, the code is dominated by this boiler plate. Every Ice source file starts with an include directive for , which contains the definitions for the Ice run time. We also include Ice.h , which was generated by the Slice compiler and contains the C++ definitions for our printer interface, and we import thePrinter.h contents of the and namespaces for brevity in the code that follows:std DemoIce 3.4.2 Documentation 34 Copyright © 2011, ZeroC, Inc. C++ #include #include using namespace std; using namespace Demo; Our server implements a single printer servant, of type . Looking at the generated code in , we find the followingPrinterI Printer.h (tidied up a little to get rid of irrelevant detail): C++ namespace Demo { class Printer : virtual public Ice::Object { public: virtual void printString(const std::string&, const Ice::Current& = Ice::Current()) = 0; }; }; The skeleton class definition is generated by the Slice compiler. (Note that the method is pure virtual so thePrinter printString skeleton class cannot be instantiated.) Our servant class inherits from the skeleton class to provide an implementation of the pure virtual method. (By convention, we use an -suffix to indicate that the class implements an interface.)printString I C++ class PrinterI : public Printer { public: virtual void printString(const string& s, const Ice::Current&); }; The implementation of the method is trivial: it simply writes its string argument to :printString stdout C++ void PrinterI:: printString(const string& s, const Ice::Current&) { cout << s << endl; } Note that has a second parameter of type . As you can see from the definition of ,printString Ice::Current Printer::printString the Slice compiler generates a default argument for this parameter, so we can leave it unused in our implementation. (We will examine the purpose of the parameter later.)Ice::Current What follows is the server main program. Note the general structure of the code:Ice 3.4.2 Documentation 35 Copyright © 2011, ZeroC, Inc. 1. C++ int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { // Server implementation here... } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) { try { ic->destroy(); } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } } return status; } The body of contains the declaration of two variables, and . The variable contains the exit status of the programmain status ic status and the variable, of type , contains the main handle to the Ice run time.ic Ice::CommunicatorPtr Following these declarations is a block in which we place all the server code, followed by two handlers. The first handler catchestry catch all exceptions that may be thrown by the Ice run time; the intent is that, if the code encounters an unexpected Ice run-time exception anywhere, the stack is unwound all the way back to , which prints the exception and then returns failure to the operating system. Themain second handler catches string constants; the intent is that, if we encounter a fatal error condition somewhere in our code, we can simply throw a string literal with an error message. Again, this unwinds the stack all the way back to , which prints the error message and thenmain returns failure to the operating system. Following the block, we see a bit of cleanup code that calls the method on the communicator (provided that the communicatortry destroy was initialized). The cleanup call is outside the first block for a reason: we must ensure that the Ice run time is finalized whether thetry code terminates normally or terminates due to an exception. Failure to call on the communicator before the program exits results in undefined behavior.destroy The body of the first block contains the actual server code:try C++ ic = Ice::initialize(argc, argv); Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice::ObjectPtr object = new PrinterI; adapter->add(object, ic->stringToIdentity("SimplePrinter")); adapter->activate(); ic->waitForShutdown(); The code goes through the following steps: We initialize the Ice run time by calling . (We pass and to this call because the server may haveIce::initialize argc argv command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns a smart pointer to an object, which is the main object in the Iceinitialize Ice::CommunicatorIce 3.4.2 Documentation 36 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. run time. We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterI object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivate have many servants that share the same adapter and do not want requests to be processed until after all the servants have been instantiated.) The server starts to process incoming requests from clients as soon as the adapter is activated. Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. (Ice provides such a helper class, called .) As far as actualIce::Application application code is concerned, the server contains only a few lines: six lines for the definition of the class, plus three lines toPrinterI instantiate a object and register it with the object adapter.PrinterI Assuming that we have the server code in a file called , we can compile it as follows:Server.cpp $ c++ -I. -I$ICE_HOME/include -c Printer.cpp Server.cpp This compiles both our application code and the code that was generated by the Slice compiler. We assume that the environmentICE_HOME variable is set to the top-level directory containing the Ice run time. (For example, if you have installed Ice in , set to/opt/Ice ICE_HOME that path.) Depending on your platform, you may have to add additional include directives or other options to the compiler; please see the demo programs that ship with Ice for the details. Finally, we need to link the server into an executable: $ c++ -o server Printer.o Server.o -L$ICE_HOME/lib -lIce -lIceUtil Again, depending on the platform, the actual list of libraries you need to link against may be longer. The demo programs that ship with Ice contain all the detail. The important point to note here is that the Ice run time is shipped in two libraries, and .libIce libIceUtil Writing and Compiling a Client in C++ The client code looks very similar to the server. Here it is in full:Ice 3.4.2 Documentation 37 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. C++ #include #include using namespace std; using namespace Demo; int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { ic = Ice::initialize(argc, argv); Ice::ObjectPrx base = ic->stringToProxy("SimplePrinter:default -p 10000"); PrinterPrx printer = PrinterPrx::checkedCast(base); if (!printer) throw "Invalid proxy"; printer->printString("Hello World!"); } catch (const Ice::Exception& ex) { cerr << ex << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) ic->destroy(); return status; } Note that the overall code layout is the same as for the server: we include the headers for the Ice run time and the header generated by the Slice compiler, and we use the same block and handlers to deal with errors.try catch The code in the block does the following:try As for the server, we initialize the Ice run time by calling .Ice::initialize The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice::ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, we need toPrinter Object do a down-cast by calling . A checked cast sends a message to the server, effectively asking "is thisPrinterPrx::checkedCast a proxy for a interface?" If so, the call returns a proxy to a ; otherwise, if the proxy denotes an interface of somePrinter Printer other type, the call returns a null proxy. We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Compiling and linking the client looks much the same as for the server: $ c++ -I. -I$ICE_HOME/include -c Printer.cpp Client.cpp $ c++ -o client Printer.o Client.o -L$ICE_HOME/lib -lIce -lIceUtil Running Client and Server in C++ To run client and server, we first start the server in a separate window:Ice 3.4.2 Documentation 38 Copyright © 2011, ZeroC, Inc. $ ./server At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ ./client $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. (We will see cleaner ways to terminate a server in our discussion of .)Ice::Application If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get: Network.cpp:471: Ice::ConnectFailedException: connect failed: Connection refused Note that, to successfully run client and server, you will have to set some platform-dependent environment variables. For example, under Linux, you need to add the Ice library directory to your . Please have a look at the demo applications that ship with IceLD_LIBRARY_PATH for the details for your platform. See Also Client-Side Slice-to-C++ Mapping Server-Side Slice-to-C++ Mapping The ClassIce::Application The Current Object IceGridIce 3.4.2 Documentation 39 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with Java This page shows how to create an Ice application with Java. On this page: Compiling a Slice Definition for Java Writing and Compiling a Server in Java Writing and Compiling a Client in Java Running Client and Server in Java Compiling a Slice Definition for Java The first step in creating our Java application is to compile our to generate Java proxies and skeletons. Under Unix, you canSlice definition compile the definition as follows: $ mkdir generated $ slice2java --output-dir generated Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The option instructs the compiler to place the generated files into the directory. This avoids cluttering the--output-dir generated working directory with the generated files. The compiler produces a number of Java source files from this definition. The exactslice2java contents of these files do not concern us for now — they contain the generated code that corresponds to the interface we definedPrinter in .Printer.ice Writing and Compiling a Server in Java To implement our interface, we must create a servant class. By convention, a servant class uses the name of its interface with an Printer -suffix, so our servant class is called and placed into a source file :I PrinterI PrinterI.java Java public class PrinterI extends Demo._PrinterDisp { public void printString(String s, Ice.Current current) { System.out.println(s); } } The class inherits from a base class called , which is generated by the compiler. The base class isPrinterI _PrinterDisp slice2java abstract and contains a method that accepts a string for the printer to print and a parameter of type . (For nowprintString Ice.Current we will ignore the parameter.) Our implementation of the method simply writes its argument to the terminal.Ice.Current printString The remainder of the server code is in a source file called , shown in full here:Server.javaIce 3.4.2 Documentation 40 Copyright © 2011, ZeroC, Inc. Java public class Server { public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(args); Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice.Object object = new PrinterI(); adapter.add(object, ic.stringToIdentity("SimplePrinter")); adapter.activate(); ic.waitForShutdown(); } catch (Ice.LocalException e) { e.printStackTrace(); status = 1; } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } } System.exit(status); } } Note the general structure of the code:Ice 3.4.2 Documentation 41 Copyright © 2011, ZeroC, Inc. 1. 2. 3. Java public class Server { public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { // Server implementation here... } catch (Ice.LocalException e) { e.printStackTrace(); status = 1; } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } } System.exit(status); } } The body of contains a block in which we place all the server code, followed by two blocks. The first block catches allmain try catch exceptions that may be thrown by the Ice run time; the intent is that, if the code encounters an unexpected Ice run-time exception anywhere, the stack is unwound all the way back to , which prints the exception and then returns failure to the operating system. The second blockmain catches exceptions; the intent is that, if we encounter a fatal error condition somewhere in our code, we can simply throw anException exception with an error message. Again, this unwinds the stack all the way back to , which prints the error message and then returnsmain failure to the operating system. Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time: the program call on any communicator it has created; otherwise, undefined behavior results.must destroy The body of our block contains the actual server code:try Java ic = Ice.Util.initialize(args); Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice.Object object = new PrinterI(); adapter.add(object, ic.stringToIdentity("SimplePrinter")); adapter.activate(); ic.waitForShutdown(); The code goes through the following steps: We initialize the Ice run time by calling . (We pass to this call because the server may haveIce.Util.initialize args command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns an reference, which is the main object in the Ice run time.initialize Ice.Communicator We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterIIce 3.4.2 Documentation 42 Copyright © 2011, ZeroC, Inc. 3. 4. 5. 6. object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivate have many servants that share the same adapter and do not want requests to be processed until after all the servants have been instantiated.) Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. (Ice provides such a helper class, called .) As far as actualIce.Application application code is concerned, the server contains only a few lines: seven lines for the definition of the class, plus three lines toPrinterI instantiate a object and register it with the object adapter.PrinterI We can compile the server code as follows: $ mkdir classes $ javac -d classes -classpath classes:$ICE_HOME/lib/Ice.jar \ Server.java PrinterI.java generated/Demo/*.java This compiles both our application code and the code that was generated by the Slice compiler. We assume that the environmentICE_HOME variable is set to the top-level directory containing the Ice run time. (For example, if you have installed Ice in , set to/opt/Ice ICE_HOME that path.) Note that Ice for Java uses the build environment to control building of source code. ( is similar to , but more flexibleant ant make for Java applications.) You can have a look at the demo code that ships with Ice to see how to use this tool. Writing and Compiling a Client in Java The client code, in , looks very similar to the server. Here it is in full:Client.javaIce 3.4.2 Documentation 43 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. public class Client { public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(args); Ice.ObjectPrx base = ic.stringToProxy("SimplePrinter:default -p 10000"); Demo.PrinterPrx printer = Demo.PrinterPrxHelper.checkedCast(base); if (printer == null) throw new Error("Invalid proxy"); printer.printString("Hello World!"); } catch (Ice.LocalException e) { e.printStackTrace(); status = 1; } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } } System.exit(status); } } Note that the overall code layout is the same as for the server: we use the same and blocks to deal with errors. The code in the try catch block does the following:try As for the server, we initialize the Ice run time by calling .Ice.Util.initialize The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice.ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, we need toPrinter Object do a down-cast by calling . A checked cast sends a message to the server, effectively askingPrinterPrxHelper.checkedCast "is this a proxy for a interface?" If so, the call returns a proxy of type ; otherwise, if the proxy denotes anPrinter Demo::Printer interface of some other type, the call returns null. We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Compiling the client looks much the same as for the server: $ javac -d classes -classpath classes:$ICE_HOME/lib/Ice.jar \ Client.java PrinterI.java generated/Demo/*.java Running Client and Server in Java To run client and server, we first start the server in a separate window:Ice 3.4.2 Documentation 44 Copyright © 2011, ZeroC, Inc. $ java Server At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ java Client $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. (We will see cleaner ways to terminate a server in our discussion of .)Ice.Application If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: Ice.ConnectionRefusedException error = 0 at IceInternal.ConnectRequestHandler.getConnection(ConnectRequestHandler.java:240) at IceInternal.ConnectRequestHandler.sendRequest(ConnectRequestHandler.java:138) at IceInternal.Outgoing.invoke(Outgoing.java:66) at Ice._ObjectDelM.ice_isA(_ObjectDelM.java:30) at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:111) at Ice.ObjectPrxHelperBase.ice_isA(ObjectPrxHelperBase.java:77) at Demo.HelloPrxHelper.checkedCast(HelloPrxHelper.java:228) at Client.run(Client.java:65) Caused by: java.net.ConnectException: Connection refused ... Note that, to successfully run client and server, your must include the Ice library and the classes directory, for example:CLASSPATH $ export CLASSPATH=$CLASSPATH:./classes:$ICE_HOME/lib/Ice.jar Please have a look at the demo applications that ship with Ice for the details for your platform. See Also Client-Side Slice-to-Java Mapping Server-Side Slice-to-Java Mapping The ClassIce.Application The Current Object IceGridIce 3.4.2 Documentation 45 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with C-Sharp This page shows how to create an Ice application with C#. On this page: Compiling a Slice Definition for C# Writing and Compiling a Server in C# Writing and Compiling a Client in C# Running Client and Server in C# Compiling a Slice Definition for C# The first step in creating our C# application is to compile our to generate C# proxies and skeletons. You can compile theSlice definition definition as follows: $ mkdir generated $ slice2cs --output-dir generated Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The option instructs the compiler to place the generated files into the directory. This avoids cluttering the--output-dir generated working directory with the generated files. The compiler produces a single source file, , from this definition. Theslice2cs Printer.cs exact contents of this file do not concern us for now — it contains the generated code that corresponds to the interface we definedPrinter in .Printer.ice Writing and Compiling a Server in C# To implement our interface, we must create a servant class. By convention, a servant class uses the name of its interface with an Printer -suffix, so our servant class is called and placed into a source file :I PrinterI Server.cs C# using System; public class PrinterI : Demo.PrinterDisp_ { public override void printString(string s, Ice.Current current) { Console.WriteLine(s); } } The class inherits from a base class called , which is generated by the compiler. The base class isPrinterI PrinterDisp_ slice2cs abstract and contains a method that accepts a string for the printer to print and a parameter of type . (For nowprintString Ice.Current we will ignore the parameter.) Our implementation of the method simply writes its argument to the terminal.Ice.Current printString The remainder of the server code follows in and is shown in full here:Server.csIce 3.4.2 Documentation 46 Copyright © 2011, ZeroC, Inc. C# public class Server { public static void Main(string[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(ref args); Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice.Object obj = new PrinterI(); adapter.add(obj, ic.stringToIdentity("SimplePrinter")); adapter.activate(); ic.waitForShutdown(); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } } Environment.Exit(status); } } Note the general structure of the code:Ice 3.4.2 Documentation 47 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. C# public class Server { public static void Main(string[] args) { int status = 0; Ice.Communicator ic = null; try { // Server implementation here... } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } } Environment.Exit(status); } } The body of contains a block in which we place all the server code, followed by a block. The catch block catches allMain try catch exceptions that may be thrown by the code; the intent is that, if the code encounters an unexpected run-time exception anywhere, the stack is unwound all the way back to , which prints the exception and then returns failure to the operating system.Main Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time: the program call on any communicator it has created; otherwise, undefined behavior results.must destroy The body of our block contains the actual server code:try C# ic = Ice.Util.initialize(ref args); Ice.ObjectAdapter adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000"); Ice.Object obj = new PrinterI(); adapter.add(obj, ic.stringToIdentity("SimplePrinter")); adapter.activate(); ic.waitForShutdown(); The code goes through the following steps: We initialize the Ice run time by calling . (We pass to this call because the server may haveIce.Util.initialize args command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns an reference, which is the main object in the Ice run time.initialize Ice.Communicator We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterI object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivate have many servants that share the same adapter and do not want requests to be processed until after all the servants have beenIce 3.4.2 Documentation 48 Copyright © 2011, ZeroC, Inc. 5. 6. 1. instantiated.) Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. (Ice provides such a helper class, called .) As far as actualIce.Application application code is concerned, the server contains only a few lines: seven lines for the definition of the class, plus three lines toPrinterI instantiate a object and register it with the object adapter.PrinterI We can compile the server code as follows: $ csc /reference:Ice.dll /lib:%ICE_HOME%\bin Server.cs generated\Printer.cs This compiles both our application code and the code that was generated by the Slice compiler. We assume that the environmentICE_HOME variable is set to the top-level directory containing the Ice run time. (For example, if you have installed Ice in , set to thatC:\Ice ICE_HOME path.) Writing and Compiling a Client in C# The client code, in , looks very similar to the server.Client.cs Here it is in full: C# using System; using Demo; public class Client { public static void Main(string[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(ref args); Ice.ObjectPrx obj = ic.stringToProxy("SimplePrinter:default -p 10000"); PrinterPrx printer = PrinterPrxHelper.checkedCast(obj); if (printer == null) throw new ApplicationException("Invalid proxy"); printer.printString("Hello World!"); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } } Environment.Exit(status); } } Note that the overall code layout is the same as for the server: we use the same and blocks to deal with errors. The code in the try catch block does the following:tryIce 3.4.2 Documentation 49 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. As for the server, we initialize the Ice run time by calling .Ice.Util.initialize The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice.ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, we need toPrinter Object do a down-cast by calling . A checked cast sends a message to the server, effectively askingPrinterPrxHelper.checkedCast "is this a proxy for a interface?" If so, the call returns a proxy of type ; otherwise, if the proxy denotes anPrinter Demo::Printer interface of some other type, the call returns null. We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Compiling the client looks much the same as for the server: $ csc /reference:Ice.dll /lib:%ICE_HOME%\bin Client.cs generated\Printer.cs Running Client and Server in C# To run client and server, we first start the server in a separate window: $ server.exe At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ client.exe $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. (We will see cleaner ways to terminate a server in our discussion of .)Ice.Application If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: Ice.ConnectionRefusedException error = 0 at IceInternal.ProxyFactory.checkRetryAfterException(LocalException ex, Reference ref, Int32 cnt) at Ice.ObjectPrxHelperBase.handleException__(ObjectDel_ delegate, LocalException ex, Int32 cnt) at Ice.ObjectPrxHelperBase.ice_isA(String id__, Dictionary`2 context__, Boolean explicitContext__) at Ice.ObjectPrxHelperBase.ice_isA(String id__) at Demo.PrinterPrxHelper.checkedCast(ObjectPrx b) at Client.Main(String[] args)Caused by: System.ComponentModel.Win32Exception: No connection could be made because the target machine actively refused it Note that, to successfully run client and server, the C# run time must be able to locate the library. (Under Windows, one way toIce.dll ensure this is to copy the library into the current directory. Please consult the documentation for your C# run time to see how it locates libraries.) See Also Client-Side Slice-to-C-Sharp Mapping Server-Side Slice-to-C-Sharp Mapping The ClassIce.Application The Current Object IceGridIce 3.4.2 Documentation 50 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with Visual Basic This page shows how to create an Ice application with Visual Basic. On this page: Visual Basic Development Process Compiling a Slice Definition for Visual Basic Writing and Compiling a Server in Visual Basic Writing and Compiling a Client in Visual Basic Running Client and Server in Visual Basic Visual Basic Development Process As of version 3.3, Ice no longer includes a separate compiler to create Visual Basic source code from Slice definitions. Instead, you need to use the Slice-to-C# compiler to create C# source code and compile the generated C# source code with a C# compiler into a DLLslice2cs that contains the compiled generated code for your Slice definitions. Your Visual Basic application then links with this DLL and the Ice for .NET DLL ( ).Ice.dll This approach works not only with Visual Basic, but with any language that targets the .NET run time. However, ZeroC does not provide support for languages other than C# and Visual Basic. The following illustration demonstrates this development process: Developing a Visual Basic application with Ice. Compiling a Slice Definition for Visual Basic The first step in creating our VB application is to compile our to generate proxies and skeletons. You can compile theSlice definition definition as follows: > mkdir generated > slice2cs --output-dir generated Printer.ice The option instructs the compiler to place the generated files into the directory. This avoids cluttering the--output-dir generated working directory with the generated files. The compiler produces a single source file, , from this definition. Theslice2cs Printer.cs exact contents of this file do not concern us for now — it contains the generated code that corresponds to the interface we definedPrinter in .Printer.ice We now need to compile this generated code into a DLL:Ice 3.4.2 Documentation 51 Copyright © 2011, ZeroC, Inc. > csc /reference:Ice.dll /lib:%ICE_HOME%\bin /t:library /out:Printer.dll generated\Printer.cs This creates a DLL called that contains the code we generated from the Slice definitions.Printer.dll Writing and Compiling a Server in Visual Basic To implement our interface, we must create a servant class. By convention, a servant class uses the name of its interface with an Printer -suffix, so our servant class is called and placed into a source file :I PrinterI Server.vb Visual Basic Imports System Imports Demo Public Class PrinterI Inherits PrinterDisp_ Public Overloads Overrides Sub printString( _ ByVal s As String, _ ByVal current As Ice.Current) Console.WriteLine(s) End Sub End Class The class inherits from a base class called , which is generated by the compiler. The base class isPrinterI PrinterDisp_ slice2cs abstract and contains a method that accepts a string for the printer to print and a parameter of type . (For nowprintString Ice.Current we will ignore the parameter.) Our implementation of the method simply writes its argument to the terminal.Ice.Current printString The remainder of the server code follows in and is shown in full here:Server.vbIce 3.4.2 Documentation 52 Copyright © 2011, ZeroC, Inc. Visual Basic Module Server Public Sub Main(ByVal args() As String) Dim status As Integer = 0 Dim ic As Ice.Communicator = Nothing Try ic = Ice.Util.initialize(args) Dim adapter As Ice.ObjectAdapter = _ ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000") Dim obj As Ice.Object = New PrinterI adapter.add(obj, ic.stringToIdentity("SimplePrinter")) adapter.activate() ic.waitForShutdown() Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try If Not ic Is Nothing Then ' Clean up ' Try ic.destroy() Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try End If Environment.Exit(status) End Sub End module Note the general structure of the code:Ice 3.4.2 Documentation 53 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. Visual Basic Module Server Public Sub Main(ByVal args() As String) Dim status As Integer = 0 Dim ic As Ice.Communicator = Nothing Try ' Server implementation here... Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try If Not ic Is Nothing Then ' Clean up ' Try ic.destroy() Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try End If Environment.Exit(status) End Sub End module The body of contains a block in which we place all the server code, followed by a block. The catch block catches allMain Try Catch exceptions that may be thrown by the code; the intent is that, if the code encounters an unexpected run-time exception anywhere, the stack is unwound all the way back to , which prints the exception and then returns failure to the operating system.Main Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time: the program call on any communicator it has created; otherwise, undefined behavior results.must destroy The body of our block contains the actual server code:Try Visual Basic ic = Ice.Util.initialize(args) Dim adapter As Ice.ObjectAdapter = _ ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000") Dim obj As Ice.Object = New PrinterI adapter.add(obj, ic.stringToIdentity("SimplePrinter")) adapter.activate() ic.waitForShutdown() The code goes through the following steps: We initialize the Ice run time by calling . (We pass to this call because the server may haveIce.Util.initialize args command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns an reference, which is the main object in the Ice run time.initialize Ice::Communicator We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterI object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivateIce 3.4.2 Documentation 54 Copyright © 2011, ZeroC, Inc. 5. 6. 1. have many servants that share the same adapter and do not want requests to be processed until after all the servants have been instantiated.) Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. (Ice provides such a helper class, called .) As far as actualIce.Application application code is concerned, the server contains only a few lines: ten lines for the definition of the class, plus three lines toPrinterI instantiate a object and register it with the object adapter.PrinterI We can compile the server code as follows: > vbc /reference:Ice.dll /libpath:%ICE_HOME%\bin /reference:Printer.dll /out:server.exe Server.vb This compiles our application code and links it with the Ice run time and the DLL we generated earlier. We assume that the ICE_HOME environment variable is set to the top-level directory containing the Ice run time. (For example, if you have installed Ice in , set C:\Ice to that path.)ICE_HOME Writing and Compiling a Client in Visual Basic The client code, in , looks very similar to the server. Here it is in full:Client.vb Visual Basic Imports System Imports Demo Module Client Public Sub Main(ByVal args() As String) Dim status As Integer = 0 Dim ic As Ice.Communicator = Nothing Try ic = Ice.Util.initialize(args) Dim obj As Ice.ObjectPrx = ic.stringToProxy("SimplePrinter:default -p 10000") Dim printer As PrinterPrx = PrinterPrxHelper.checkedCast(obj) If printer Is Nothing Then Throw New ApplicationException("Invalid proxy") End If printer.printString("Hello World!") Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try If Not ic Is Nothing Then ' Clean up ' Try ic.destroy() Catch e As Exception Console.Error.WriteLine(e) status = 1 End Try End If Environment.Exit(status) End Sub End Module Note that the overall code layout is the same as for the server: we use the same and blocks to deal with errors. The code in the Try Catch block does the following:TryIce 3.4.2 Documentation 55 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. As for the server, we initialize the Ice run time by calling .Ice.Util.initialize The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice.ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, we need toPrinter Object do a down-cast by calling . A checked cast sends a message to the server, effectively askingPrinterPrxHelper.checkedCast "is this a proxy for a interface?" If so, the call returns a proxy of type ; otherwise, if the proxy denotes anPrinter Demo::Printer interface of some other type, the call returns null. We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Compiling the client looks much the same as for the server: > vbc /reference:Ice.dll /libpath:%ICE_HOME%\bin /reference:Printer.dll /out:client.exe Client.vb Running Client and Server in Visual Basic To run client and server, we first start the server in a separate window: > server.exe At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: > client.exe > The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. (We will see cleaner ways to terminate a server in our discussion of .)Ice.Application If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: Ice.ConnectionRefusedException error = 0 at IceInternal.ProxyFactory.checkRetryAfterException(LocalException ex, Reference ref, Int32 cnt) at Ice.ObjectPrxHelperBase.handleException__(ObjectDel_ delegate, LocalException ex, Int32 cnt) at Ice.ObjectPrxHelperBase.ice_isA(String id__, Dictionary`2 context__, Boolean explicitContext__) at Ice.ObjectPrxHelperBase.ice_isA(String id__) at Demo.PrinterPrxHelper.checkedCast(ObjectPrx b) at Client.Main(String[] args)Caused by: System.ComponentModel.Win32Exception: No connection could be made because the target machine actively refused it Note that, to successfully run client and server, the VB run time must be able to locate the library. (Under Windows, one way toIce.dll ensure this is to copy the library into the current directory. Please consult the documentation for your VB run time to see how it locates libraries.) See Also Client-Side Slice-to-C-Sharp Mapping Server-Side Slice-to-C-Sharp Mapping The ClassIce.Application The Current Object IceGridIce 3.4.2 Documentation 56 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with Objective-C This page shows how to create an Ice application with Objective-C. On this page: Compiling a Slice Definition for Objective-C Writing and Compiling a Server in Objective-C Writing and Compiling a Client in Objective-C Running Client and Server in Objective-C Compiling a Slice Definition for Objective-C The first step in creating our Objective-C application is to compile our to generate Objective-C proxies and skeletons. UnderSlice definition Unix, you can compile the definition as follows: $ slice2objc Printer.ice The compiler produces two Objective-C source files from this definition, and .slice2objc Printer.h Printer.m Printer.h The header file contains Objective-C type definitions that correspond to the Slice definitions for our interface.Printer.h Printer This header file must be included in both the client and the server source code. Printer.m The file contains the source code for our interface. The generated source contains type-specific run-timePrinter.m Printer support for both clients and servers. For example, it contains code that marshals parameter data (the string passed to the operation) on the client side and unmarshals that data on the server side.printString The file must be compiled and linked into both client and server.Printer.m Writing and Compiling a Server in Objective-C The source code for the server takes only a few lines and is shown in full here:Ice 3.4.2 Documentation 57 Copyright © 2011, ZeroC, Inc. Objective-C #import #import #import #import @interface PrinterI : DemoPrinter @end @implementation PrinterI -(void) printString:(NSMutableString *)s current:(ICECurrent *)current { printf("%s\n", [s UTF8String]); } @end int main(int argc, char* argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int status = 1; id communicator = nil; @try { communicator = [ICEUtil createCommunicator:&argc argv:argv]; id adapter = [communicator createObjectAdapterWithEndpoints: @"SimplePrinterAdapter" endpoints:@"default -p 10000"]; ICEObject *object = [[[PrinterI alloc] init] autorelease]; [adapter add:object identity:[communicator stringToIdentity:@"SimplePrinter"]]; [adapter activate]; [communicator waitForShutdown]; status = 0; } @catch (NSException* ex) { NSLog(@"%@", ex); } @try { [communicator destroy]; } @catch (NSException* ex) { NSLog(@"%@", ex); } [pool release]; return status; } There appears to be a lot of code here for something as simple as a server that just prints a string. Do not be concerned by this: most of the preceding code is boiler plate that never changes. For this very simple server, the code is dominated by this boiler plate. Every Ice source file starts with an include directive for , which contains the definitions for the Ice run time. We also include Ice.h , which was generated by the Slice compiler and contains the Objective-C definitions for our printer interface. In addition, wePrinter.h import headers to allow us to use an autorelease pool and to produce output:Ice 3.4.2 Documentation 58 Copyright © 2011, ZeroC, Inc. Objective-C #import #import #import #import Our server implements a single printer servant, of type . Looking at the generated code in , we find the followingPrinterI Printer.h (tidied up a little to get rid of irrelevant detail): Objective-C @protocol DemoPrinter -(void) printString:(NSMutableString *)s current:(ICECurrent *)current; @end @interface DemoPrinter : ICEObject // ... @end The protocol and class definitions are generated by the Slice compiler. The protocol defines the method,DemoPrinter printString which we must implement in our servant. The class contains methods that are internal to the mapping, so we are notDemoPrinter concerned with these. However, our servant must derive from this skeleton class: Objective-C @interface PrinterI : DemoPrinter @end @implementation PrinterI -(void) printString:(NSMutableString *)s current:(ICECurrent *)current { printf("%s\n", [s UTF8String]); } @end As you can see, the implementation of the method is trivial: it simply writes its string argument to .printString stdout Note that has a second parameter of type . The Ice run time passes additional information about an incomingprintString ICECurrent request to the servant in this parameter. For now, we will ignore it. What follows is the server main program. Note the general structure of the code:Ice 3.4.2 Documentation 59 Copyright © 2011, ZeroC, Inc. 1. Objective-C int main(int argc, char* argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int status = 1; id communicator = nil; @try { communicator = [ICEUtil createCommunicator:&argc argv:argv]; // Server implementation here... status = 0; } @catch (NSException* ex) { NSLog(@"%@", ex); } @try { [communicator destroy]; } @catch (NSException* ex) { NSLog(@"%@", ex); } [pool release]; return status; } The body of instantiates an autorelease pool, which it releases before returning to ensure that the program does not leak memory. main contains the declaration of two variables, and . The variable contains the exit status of the programmain status communicator status and the variable, of type , contains the main handle to the Ice run time.communicator id Following these declarations is a block in which we place all the server code, followed by a handler that logs any unhandledtry catch exceptions. Before returning, executes a bit of cleanup code that calls the method on the communicator. The cleanup call is outside themain destroy first block for a reason: we must ensure that the Ice run time is finalized whether the code terminates normally or terminates due to antry exception. Failure to call on the communicator before the program exits results in undefined behavior.destroy The body of the first block contains the actual server code:try Objective-C communicator = [ICEUtil createCommunicator:&argc argv:argv]; id adapter = [communicator createObjectAdapterWithEndpoints: @"SimplePrinterAdapter" endpoints:@"default -p 10000"]; ICEObject *object = [[[PrinterI alloc] init] autorelease]; [adapter add:object identity:[communicator stringToIdentity:@"SimplePrinter"]]; [adapter activate]; [communicator waitForShutdown]; The code goes through the following steps: We initialize the Ice run time by calling . (We pass and to this call because the server may havecreateCommunicator argc argvIce 3.4.2 Documentation 60 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns a value of type , which is the main object in thecreateCommunicator id Ice run time. We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterI object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivate have many servants that share the same adapter and do not want requests to be processed until after all the servants have been instantiated.) The server starts to process incoming requests from clients as soon as the adapter is activated. Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. As far as actual application code is concerned, the server contains only a few lines: nine lines for the definition of the class, plus three lines to instantiate a object and register it with the object adapter.PrinterI PrinterI Assuming that we have the server code in a file called , we can compile it as follows:Server.m $ cc -c -I. -I$ICE_HOME/include Printer.m Server.m This compiles both our application code and the code that was generated by the Slice compiler. We assume that the environmentICE_HOME variable is set to the top-level directory containing the Ice run time. (For example, if you have installed Ice in , set to/opt/Ice ICE_HOME that path.) Depending on your platform, you may have to add additional include directives or other options to the compiler; please see the demo programs that ship with Ice for the details. Finally, we need to link the server into an executable: $ c++ Printer.o Server.o -o server -L$ICE_HOME/lib -lIceObjC -framework Foundation Again, depending on the platform, the actual list of libraries you need to link against may be longer. The demo programs that ship with Ice contain all the detail. Writing and Compiling a Client in Objective-C The client code looks very similar to the server. Here it is in full:Ice 3.4.2 Documentation 61 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. Objective-C #import #import #import #import int main(int argc, char* argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int status = 1; id communicator = nil; @try { communicator = [ICEUtil createCommunicator:&argc argv:argv]; id base = [communicator stringToProxy:@"SimplePrinter:default -p 10000"]; id printer = [DemoPrinterPrx checkedCast:base]; if(!printer) [NSException raise:@"Invalid proxy" format:nil]; [printer printString:@"Hello World!"]; status = 0; } @catch (NSException* ex) { NSLog(@"%@", ex); } @try { [communicator destroy]; } @catch (NSException* ex) { NSLog(@"%@", ex); } [pool release]; return status; } Note that the overall code layout is the same as for the server: we include the headers for the Ice run time and the header generated by the Slice compiler, and we use the same block and handlers to deal with errors.try catch The code in the block does the following:try As for the server, we initialize the Ice run time by calling .createCommunicator The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfacesstringToProxy id and classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, wePrinter Object need to do a down-cast by calling the class method on the class. A checked cast sends acheckedCast DemoPrinterPrx message to the server, effectively asking "is this a proxy for a interface?" If so, the call returns a proxy to a ;Printer Printer otherwise, if the proxy denotes an interface of some other type, the call returns a null proxy. We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Compiling and linking the client looks much the same as for the server: $ cc -c -I. -I$ICE_HOME/include Printer.m Client.m $ c++ Printer.o Client.o -o client -L$ICE_HOME/lib -lIceObjC -framework FoundationIce 3.4.2 Documentation 62 Copyright © 2011, ZeroC, Inc. Running Client and Server in Objective-C To run client and server, we first start the server in a separate window: $ ./server At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ ./client $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get: Network.cpp:1218: Ice::ConnectionRefusedException: connection refused: Connection refused Note that, to successfully run client and server, you may have to set to include the Ice library directory. Please seeDYLD_LIBRARY_PATH the installation instructions and the demo applications that ship with Ice Touch for details. See Also Client-Side Slice-to-Objective-C Mapping Server-Side Slice-to-Objective-C Mapping The Current Object IceGridIce 3.4.2 Documentation 63 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with Python This page shows how to create an Ice application with Python. On this page: Compiling a Slice Definition for Python Writing a Server in Python Writing a Client in Python Running Client and Server in Python Compiling a Slice Definition for Python The first step in creating our Python application is to compile our to generate Python proxies and skeletons. You can compileSlice definition the definition as follows: $ slice2py Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The compiler produces a single source file, , from this definition. The compiler also creates a Python packageslice2py Printer_ice.py for the module, resulting in a subdirectory named . The exact contents of the source file do not concern us for now — it containsDemo Demo the generated code that corresponds to the interface we defined in .Printer Printer.ice Writing a Server in Python To implement our interface, we must create a servant class. By convention, a servant class uses the name of its interface with an Printer -suffix, so our servant class is called :I PrinterI Python class PrinterI(Demo.Printer): def printString(self, s, current=None): print s The class inherits from a base class called , which is generated by the compiler. The base class isPrinterI Demo.Printer slice2py abstract and contains a method that accepts a string for the printer to print and a parameter of type . (For nowprintString Ice.Current we will ignore the parameter.) Our implementation of the method simply writes its argument to the terminal.Ice.Current printString The remainder of the server code, in , follows our servant class and is shown in full here:Server.pyIce 3.4.2 Documentation 64 Copyright © 2011, ZeroC, Inc. Python import sys, traceback, Ice import Demo class PrinterI(Demo.Printer): def printString(self, s, current=None): print s status = 0 ic = None try: ic = Ice.initialize(sys.argv) adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000") object = PrinterI() adapter.add(object, ic.stringToIdentity("SimplePrinter")) adapter.activate() ic.waitForShutdown() except: traceback.print_exc() status = 1 if ic: # Clean up try: ic.destroy() except: traceback.print_exc() status = 1 sys.exit(status) Note the general structure of the code: Python status = 0 ic = None try: # Server implementation here... except: traceback.print_exc() status = 1 if ic: # Clean up try: ic.destroy() except: traceback.print_exc() status = 1 sys.exit(status) The body of the main program contains a block in which we place all the server code, followed by an block. The blocktry except except catches all exceptions that may be thrown by the code; the intent is that, if the code encounters an unexpected run-time exception anywhere, the stack is unwound all the way back to the main program, which prints the exception and then returns failure to the operating system. Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time: the program call on any communicator it has created; otherwise, undefined behavior results.must destroy The body of our block contains the actual server code:tryIce 3.4.2 Documentation 65 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. Python ic = Ice.initialize(sys.argv) adapter = ic.createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000") object = PrinterI() adapter.add(object, ic.stringToIdentity("SimplePrinter")) adapter.activate() ic.waitForShutdown() The code goes through the following steps: We initialize the Ice run time by calling . (We pass to this call because the server may haveIce.initialize sys.argv command-line arguments that are of interest to the run time; for this example, the server does not require any command-line arguments.) The call to returns an reference, which is the main object in the Ice run time.initialize Ice.Communicator We create an object adapter by calling on the instance. The argumentscreateObjectAdapterWithEndpoints Communicator we pass are (which is the name of the adapter) and , which instructs the"SimplePrinterAdapter" "default -p 10000" adapter to listen for incoming requests using the default protocol (TCP/IP) at port number 10000. At this point, the server-side run time is initialized and we create a servant for our interface by instantiating a Printer PrinterI object. We inform the object adapter of the presence of a new servant by calling on the adapter; the arguments to are the servantadd add we have just instantiated, plus an identifier. In this case, the string is the name of the servant. (If we had"SimplePrinter" multiple printers, each would have a different name or, more correctly, a different .)object identity Next, we activate the adapter by calling its method. (The adapter is initially created in a holding state; this is useful if weactivate have many servants that share the same adapter and do not want requests to be processed until after all the servants have been instantiated.) Finally, we call . This call suspends the calling thread until the server implementation terminates, either bywaitForShutdown making a call to shut down the run time, or in response to a signal. (For now, we will simply interrupt the server on the command line when we no longer need it.) Note that, even though there is quite a bit of code here, that code is essentially the same for all servers. You can put that code into a helper class and, thereafter, will not have to bother with it again. (Ice provides such a helper class, called .) As far as actualIce.Application application code is concerned, the server contains only a few lines: three lines for the definition of the class, plus two lines toPrinterI instantiate a object and register it with the object adapter.PrinterI Writing a Client in Python The client code, in , looks very similar to the server. Here it is in full:Client.pyIce 3.4.2 Documentation 66 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. Python import sys, traceback, Ice import Demo status = 0 ic = None try: ic = Ice.initialize(sys.argv) base = ic.stringToProxy("SimplePrinter:default -p 10000") printer = Demo.PrinterPrx.checkedCast(base) if not printer: raise RuntimeError("Invalid proxy") printer.printString("Hello World!") except: traceback.print_exc() status = 1 if ic: # Clean up try: ic.destroy() except: traceback.print_exc() status = 1 sys.exit(status) Note that the overall code layout is the same as for the server: we use the same and blocks to deal with errors. The code in thetry except block does the following:try As for the server, we initialize the Ice run time by calling .Ice.initialize The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice.ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, weDemo::Printer Object need to do a down-cast by calling . A checked cast sends a message to the server, effectivelyDemo.PrinterPrx.checkedCast asking "is this a proxy for a interface?" If so, the call returns a proxy of type ; otherwise, if theDemo::Printer Demo.PrinterPrx proxy denotes an interface of some other type, the call returns .None We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Running Client and Server in Python To run client and server, we first start the server in a separate window: $ python Server.py At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ python Client.py $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line for now. (We will see cleaner ways to terminate a server in our discussion of .)Ice.ApplicationIce 3.4.2 Documentation 67 Copyright © 2011, ZeroC, Inc. If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: Traceback (most recent call last): File "Client.py", line 10, in ? printer = Demo.PrinterPrx.checkedCast(base) File "Printer_ice.py", line 43, in checkedCast return Demo.PrinterPrx.ice_checkedCast(proxy, '::Demo::Printer', facet) ConnectionRefusedException: Ice.ConnectionRefusedException: Connection refused Note that, to successfully run the client and server, the Python interpreter must be able to locate the Ice extension for Python. See the Ice for Python installation instructions for more information. See Also Client-Side Slice-to-Python Mapping Server-Side Slice-to-Python Mapping The ClassIce.Application The Current Object IceGridIce 3.4.2 Documentation 68 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with Ruby This page shows how to create an Ice client application with Ruby. On this page: Compiling a Slice Definition for Ruby Writing a Client in Ruby Running the Client in Ruby Compiling a Slice Definition for Ruby The first step in creating our Ruby application is to compile our to generate Ruby proxies. You can compile the definition asSlice definition follows: $ slice2rb Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The compiler produces a single source file, , from this definition. The exact contents of the source file do notslice2rb Printer.rb concern us for now — it contains the generated code that corresponds to the interface we defined in .Printer Printer.ice Writing a Client in Ruby The client code, in , is shown below in full:Client.rbIce 3.4.2 Documentation 69 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. Ruby require 'Printer.rb' status = 0 ic = nil begin ic = Ice::initialize(ARGV) base = ic.stringToProxy("SimplePrinter:default -p 10000") printer = Demo::PrinterPrx::checkedCast(base) if not printer raise "Invalid proxy" end printer.printString("Hello World!") rescue puts $! puts $!.backtrace.join("\n") status = 1 end if ic # Clean up begin ic.destroy() rescue puts $! puts $!.backtrace.join("\n") status = 1 end end exit(status) The program begins with a statement, which loads the Ruby code we generated from our Slice definition in the previous section. Itrequire is not necessary for the client to explicitly load the module because does that for you.Ice Printer.rb The body of the main program contains a block in which we place all the client code, followed by a block. The blockbegin rescue rescue catches all exceptions that may be thrown by the code; the intent is that, if the code encounters an unexpected run-time exception anywhere, the stack is unwound all the way back to the main program, which prints the exception and then returns failure to the operating system. The body of our block goes through the following steps:begin We initialize the Ice run time by calling . (We pass to this call because the client may have command-lineIce::initialize ARGV arguments that are of interest to the run time; for this example, the client does not require any command-line arguments.) The call to returns an reference, which is the main object in the Ice run time.initialize Ice::Communicator The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice::ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, weDemo::Printer Object need to do a down-cast by calling . A checked cast sends a message to the server,Demo::PrinterPrx::checkedCast effectively asking "is this a proxy for a interface?" If so, the call returns a proxy of type ;Demo::Printer Demo::PrinterPrx otherwise, if the proxy denotes an interface of some other type, the call returns .nil We test that the down-cast succeeded and, if not, throw an error message that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time: the program call on any communicator it has created; otherwise, undefined behavior results.must destroy Running the Client in Ruby The server must be started before the client. Since Ice for Ruby does not support server-side behavior, we need to use a server fromIce 3.4.2 Documentation 70 Copyright © 2011, ZeroC, Inc. another language mapping. In this case, we will use the :C++ server $ server At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window: $ ruby Client.rb $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line. If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: exception ::Ice::ConnectionRefusedException { error = 111 } Note that, to successfully run the client, the Ruby interpreter must be able to locate the Ice extension for Ruby. See the Ice for Ruby installation instructions for more information. See Also Client-Side Slice-to-Ruby Mapping IceGridIce 3.4.2 Documentation 71 Copyright © 2011, ZeroC, Inc. Writing an Ice Application with PHP This page shows how to create an Ice client application with PHP. On this page: Compiling a Slice Definition for PHP Writing a Client in PHP Running the Client in PHP Compiling a Slice Definition for PHP The first step in creating our PHP application is to compile our to generate PHP code. You can compile the definition asSlice definition follows: $ slice2php Printer.ice Whenever we show Unix commands, we assume a Bourne or Bash shell. The commands for Windows are essentially identical and therefore not shown. The compiler produces a single source file, , from this definition. The exact contents of the source file do notslice2php Printer.php concern us for now — it contains the generated code that corresponds to the interface we defined in .Printer Printer.ice Writing a Client in PHP The client code, in , is shown below in full:Client.phpIce 3.4.2 Documentation 72 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. PHP stringToProxy("SimplePrinter:default -p 10000"); $printer = Demo_PrinterPrxHelper::checkedCast($base); if(!$printer) throw new RuntimeException("Invalid proxy"); $printer->printString("Hello World!"); } catch(Exception $ex) { echo $ex; } if($ic) { // Clean up try { $ic->destroy(); } catch(Exception $ex) { echo $ex; } } ?> The program begins with statements to load the Ice run-time definitions ( ) and the code we generated from our Slicerequire Ice.php definition in the previous section ( ).Printer.php The body of the main program contains a block in which we place all the client code, followed by a block. The blocktry catch catch catches all exceptions that may be thrown by the code; the intent is that, if the code encounters an unexpected run-time exception anywhere, the stack is unwound all the way back to the main program, which prints the exception and then returns failure to the operating system. The body of our block goes through the following steps:try We initialize the Ice run time by calling . The call to returns an reference,Ice_initialize initialize Ice_Communicator which is the main object in the Ice run time. The next step is to obtain a proxy for the remote printer. We create a proxy by calling on the communicator, withstringToProxy the string . Note that the string contains the object identity and the port number that were"SimplePrinter:default -p 10000" used by the server. (Obviously, hard-coding object identities and port numbers into our applications is a bad idea, but it will do for now; we will see more architecturally sound ways of doing this when we discuss .)IceGrid The proxy returned by is of type , which is at the root of the inheritance tree for interfaces andstringToProxy Ice_ObjectPrx classes. But to actually talk to our printer, we need a proxy for a interface, not an interface. To do this, weDemo::Printer Object need to do a down-cast by calling . A checked cast sends a message to the server,Demo_PrinterPrxHelper::checkedCast effectively asking "is this a proxy for a interface?" If so, the call returns a proxy narrowed to the Demo::Printer Printer interface; otherwise, if the proxy denotes an interface of some other type, the call returns .null We test that the down-cast succeeded and, if not, throw an exception that terminates the client. We now have a live proxy in our address space and can call the method, passing it the time-honored printString "Hello string. The server prints that string on its terminal.World!" Before the code exits, it destroys the communicator (if one was created successfully). Doing this is essential in order to correctly finalize the Ice run time. If a script neglects to destroy the communicator, Ice destroys it automatically. Running the Client in PHPIce 3.4.2 Documentation 73 Copyright © 2011, ZeroC, Inc. The server must be started before the client. Since Ice for PHP does not support server-side behavior, we need to use a server from another language mapping. In this case, we will use the :C++ server $ server At this point, we won't see anything because the server simply waits for a client to connect to it. We run the client in a different window using PHP's command-line interpreter: $ php -f Client.php $ The client runs and exits without producing any output; however, in the server window, we see the that is produced by"Hello World!" the printer. To get rid of the server, we interrupt it on the command line. If anything goes wrong, the client will print an error message. For example, if we run the client without having first started the server, we get something like the following: exception ::Ice::ConnectionRefusedException { error = 111 } Note that, to successfully run the client, the PHP interpreter must be able to locate the Ice extension for PHP. See the Ice for PHP installation instructions for more information. See Also Client-Side Slice-to-PHP Mapping IceGridIce 3.4.2 Documentation 74 Copyright © 2011, ZeroC, Inc. The Slice Language Here, we present the Slice language. Slice (Specification Language for Ice) is the fundamental abstraction mechanism for separating object interfaces from their implementations. Slice establishes a contract between client and server that describes the types and object interfaces used by an application. This description is independent of the implementation language, so it does not matter whether the client is written in the same language as the server. Even though Slice is an acronym, it is pronounced as a single syllable, like a slice of bread. Slice definitions are compiled for a particular implementation language by a compiler. The compiler translates the language-independent definitions into language-specific type definitions and APIs. These types and APIs are used by the developer to provide application functionality and to interact with Ice. The translation algorithms for various implementation languages are known as .language mappings Currently, Ice defines language mappings for C++, Java, C#, Python, Objective-C, Ruby, and PHP. Because Slice describes interfaces and types (but not implementations), it is a purely declarative language; there is no way to write executable statements in Slice. Slice definitions focus on object interfaces, the operations supported by those interfaces, and exceptions that may be raised by operations. In addition, Slice offers features for . This requires quite a bit of supporting machinery; in particular, much of Slice isobject persistence concerned with the definition of data types. This is because data can be exchanged between client and server only if their types are defined in Slice. You cannot exchange arbitrary C++ data between client and server because it would destroy the language independence of Ice. However, you can always create a Slice type definition that corresponds to the C++ data you want to send, and then you can transmit the Slice type. We present the full syntax and semantics of Slice here. Because much of Slice is based on C++ and Java, we focus on those areas where Slice differs from C++ or Java or constrains the equivalent C++ or Java feature in some way. Slice features that are identical to C++ and Java are mentioned mostly by example. Topics Slice Compilation Slice Source Files Lexical Rules Modules Basic Types User-Defined Types Interfaces, Operations, and Exceptions Classes Forward Declarations Type IDs Operations on Object Local Types Names and Scoping Metadata Serializable Objects Deprecating Slice Definitions Using the Slice Compilers Slice Checksums Generating Slice Documentation Slice Keywords Slice Metadata Directives Slice for a Simple File SystemIce 3.4.2 Documentation 75 Copyright © 2011, ZeroC, Inc. Slice Compilation On this page: Compilation Single Development Environment for Client and Server Different Development Environments for Client and Server Compilation A Slice compiler produces source files that must be combined with application code to produce client and server executables. The outcome of the development process is a client executable and a server executable. These executables can be deployed anywhere, whether the target environments use the same or different operating systems and whether the executables are implemented using the same or different languages. The only constraint is that the host machines must provide the necessary run-time environment, such as any required dynamic libraries, and that connectivity can be established between them. Single Development Environment for Client and Server The figure below shows the situation when both client and server are developed in C++. The Slice compiler generates two files from a Slice definition in a source file : a header file ( ) and a source file ( )Printer.ice Printer.h Printer.cpp . Development process if client and server share the same development environment. The header file contains definitions that correspond to the types used in the Slice definition. It is included in the sourcePrinter.h code of both client and server to ensure that client and server agree about the types and interfaces used by the application. The source file provides an API to the client for sending messages to remote objects. The client source code (Printer.cpp , written by the client developer) contains the client-side application logic. The generated source code and the clientClient.cpp code are compiled and linked into the client executable. The source file also contains source code that provides an up-call interface from the Ice run time into the server code writtenPrinter.cppIce 3.4.2 Documentation 76 Copyright © 2011, ZeroC, Inc. by the developer and provides the connection between the networking layer of Ice and the application code. The server implementation file ( , written by the server developer) contains the server-side application logic (the object implementations, properly termed Server.cpp ). The generated source code and the implementation source code are compiled and linked into the server executable.servants Both client and server also link with an Ice library that provides the necessary run-time support. You are not limited to a single implementation of a client or server. For example, you can build multiple servers, each of which implements the same interfaces but uses different implementations (for example, with different performance characteristics). Multiple such server implementations can coexist in the same system. This arrangement provides one fundamental scalability mechanism in Ice: if you find that a server process starts to bog down as the number of objects increases, you can run an additional server for the same interfaces on a different machine. Such servers provide a single logical service that is distributed over a number of processes on different machines. Eachfederated server in the federation implements the same interfaces but hosts different object instances. (Of course, federated servers must somehow ensure consistency of any databases they share across the federation.) Ice also provides support for servers. Replication permits multiple servers to each implement the same set of object instances.replicated This improves performance and scalability (because client load can be shared over a number of servers) as well as redundancy (because each object is implemented in more than one server). Different Development Environments for Client and Server Client and server cannot share any source or binary components if they are developed in different languages. For example, a client written in Java cannot include a C++ header file. This figure shows the situation when a client written in Java and the corresponding server is written in C++. In this case, the client and server developers are completely independent, and each uses his or her own development environment and language mapping. The only link between client and server developers is the Slice definition each one uses. Development process for different development environments. For Java, the slice compiler creates a number of files whose names depend on the names of various Slice constructs. (These files are collectively referred to as in the above figure.)*.java See Also Using the Slice CompilersIce 3.4.2 Documentation 77 Copyright © 2011, ZeroC, Inc. Slice Source Files Slice defines a number of rules for the naming and contents of Slice source files. On this page: File Naming File Format Preprocessing Definition Order File Naming Files containing Slice definitions must end in a file extension, for example, is a valid file name. Other file extensions are.ice Clock.ice rejected by the compilers. For case-insensitive file systems (such as DOS), the file extension may be written as uppercase or lowercase, so is legal. ForClock.ICE case-sensitive file systems (such as Unix), is illegal. (The extension must be in lowercase.)Clock.ICE File Format Slice is a free-form language so you can use spaces, horizontal and vertical tab stops, form feeds, and newline characters to lay out your code in any way you wish. (White space characters are token separators). Slice does not attach semantics to the layout of a definition. You may wish to follow the style we have used for the Slice examples throughout this book. Slice files can be ASCII text files or use the UTF-8 character encoding with a byte order marker (BOM) at the beginning of each file. However, Slice identifiers are limited to ASCII letters and digits; non-ASCII letters can appear only in comments. Preprocessing Slice is preprocessed by the C++ preprocessor, so you can use the usual preprocessor directives, such as and macro definitions.#include However, Slice permits directives only at the beginning of a file, before any Slice definitions.#include If you use directives, it is a good idea to protect them with guards to prevent double inclusion of a file:#include Slice // File Clock.ice #ifndef _CLOCK_ICE #define _CLOCK_ICE // #include directives here... // Definitions here... #endif _CLOCK_ICE #include directives permit a Slice definition to use types defined in a different source file. The Slice compilers parse all of the code in a source file, including the code in subordinate files. However, the compilers generate code only for the top-level file(s) nominated#include on the command line. You must separately compile subordinate files to obtain generated code for all the files that make up your#include Slice definition. Note that you should avoid with double quotes:#include Slice #include "Clock.ice" // Not recommended! While double quotes will work, the directory in which the preprocessor tries to locate the file can vary depending on the operating system, so the included file may not always be found where you expect it. Instead, use angle brackets ( ); you can control which directories are<> searched for the file with the of the Slice compiler. option-IIce 3.4.2 Documentation 78 Copyright © 2011, ZeroC, Inc. Also note that, if you include a path separator in a directive, you must use a forward slash:#include Slice #include // OK You cannot use a backslash in directives:#include Slice #include // Illegal Definition Order Slice constructs, such as modules, interfaces, or type definitions, can appear in any order you prefer. However, identifiers must be declared before they can be used. See Also Using the Slice CompilersIce 3.4.2 Documentation 79 Copyright © 2011, ZeroC, Inc. Lexical Rules Slice's lexical rules are very similar to those of C++ and Java, except for some differences for identifiers. On this page: Comments Keywords Identifiers Case Sensitivity Identifiers That Are Keywords Escaped Identifiers Reserved Identifiers Comments Slice definitions permit both the C and the C++ style of writing comments: Slice /* * C-style comment. */ // C++-style comment extending to the end of this line. Keywords Slice uses a number of , which must be spelled in lowercase. For example, and are keywords and must bekeywords class dictionary spelled as shown. There are two exceptions to this lowercase rule: and are keywords and must be capitalized asObject LocalObject shown. Identifiers Identifiers begin with an alphabetic character followed by any number of alphabetic characters or digits. Underscores are also permitted in identifiers with the following limitations: an identifier cannot begin or end with an underscore an identifier cannot contain multiple consecutive underscores Given these rules, the identifier is legal but not , , or .get_account_name _account account_ get__account Slice identifiers are restricted to the ASCII range of alphabetic characters and cannot contain non-English letters, such as Å. (Supporting non-ASCII identifiers would make it very difficult to map Slice to target languages that lack support for this feature.) Case Sensitivity Identifiers are case-insensitive but must be capitalized consistently. For example, and are considered the sameTimeOfDay TIMEOFDAY identifier within a naming scope. However, Slice enforces consistent capitalization. After you have introduced an identifier, you must capitalize it consistently throughout; otherwise, the compiler will reject it as illegal. This rule exists to permit mappings of Slice to languages that ignore case in identifiers as well as to languages that treat differently capitalized identifiers as distinct. Identifiers That Are Keywords You can define Slice identifiers that are keywords in one or more implementation languages. For example, is a perfectly good Sliceswitch identifier but is a C++ and Java keyword. Each language mapping defines rules for dealing with such identifiers. The solution typically involves using a prefix to map away from the keyword. For example, the Slice identifier is mapped to in C++ and switch _cpp_switch in Java._switch The rules for dealing with keywords can result in hard-to-read source code. Identifiers such as , , or will clash withnative throw exportIce 3.4.2 Documentation 80 Copyright © 2011, ZeroC, Inc. C++ or Java keywords (or both). To make life easier for yourself and others, try to avoid Slice identifiers that are implementation language keywords. Keep in mind that mappings for new languages may be added to Ice in the future. While it is not reasonable to expect you to compile a list of all keywords in all popular programming languages, you should make an attempt to avoid at least common keywords. Slice identifiers such as , , and are definitely not a good idea.self import while Escaped Identifiers It is possible to use a Slice keyword as an identifier by prefixing the keyword with a backslash, for example: Slice struct dictionary { // Error! // ... }; struct \dictionary { // OK // ... }; struct \foo { // Legal, same as "struct foo" // ... }; he backslash escapes the usual meaning of a keyword; in the preceding example, is treated as the identifier .\dictionary dictionary The escape mechanism exists to permit keywords to be added to the Slice language over time with minimal disruption to existing specifications: if a pre-existing specification happens to use a newly-introduced keyword, that specification can be fixed by simply prepending a backslash to the new keyword. Note that, as a matter of style, you should avoid using Slice keywords as identifiers (even though the backslash escapes allow you to do this). It is legal (though redundant) to precede an identifier that is not a keyword with a backslash — the backslash is ignored in that case. Reserved Identifiers Slice reserves the identifier and all identifiers beginning with (in any capitalization) for the Ice implementation. For example, if youIce Ice try to define a type named , the Slice compiler will issue an error message.Icecream You can suppress this behavior by using the compiler option, which enables definition of identifiers beginning with --ice . However, do not use this option unless you are compiling the Slice definitions for the Ice run time itself.Ice Slice identifiers ending in any of the suffixes , , , and are also reserved. These endings are used by the variousHelper Holder Prx Ptr language mappings and are reserved to prevent name clashes in the generated code. See Also Slice KeywordsIce 3.4.2 Documentation 81 Copyright © 2011, ZeroC, Inc. Modules On this page: Modules Reduce Clutter Modules are Mandatory Reopening Modules Module Mapping The Ice Module Modules Reduce Clutter A common problem in large systems is pollution of the global namespace: over time, as isolated systems are integrated, name clashes become quite likely. Slice provides the construct to alleviate this problem:module Slice module ZeroC { module Client { // Definitions here... }; module Server { // Definitions here... }; }; A module can contain any legal Slice construct, including other module definitions. Using modules to group related definitions together avoids polluting the global namespace and makes accidental name clashes quite unlikely. (You can use a well-known name, such as a company or product name, as the name of the outermost module.) Modules are Mandatory Slice requires all definitions to be nested inside a module, that is, you cannot define anything other than a module at global scope. For example, the following is illegal: Slice interface I { // Error: only modules can appear at global scope // ... }; Definitions at global scope are prohibited because they cause problems with some implementation languages (such as Python, which does not have a true global scope). Throughout the Ice manual, you will occasionally see Slice definitions that are not nested inside a module. This is to keep the examples short and free of clutter. Whenever you see such a definition, assume that it is nested in a module. Reopening Modules Modules can be reopened:Ice 3.4.2 Documentation 82 Copyright © 2011, ZeroC, Inc. Slice module ZeroC { // Definitions here... }; // Possibly in a different source file: module ZeroC { // OK, reopened module // More definitions here... }; Reopened modules are useful for larger projects: they allow you to split the contents of a module over several different . Thesource files advantage of doing this is that, when a developer makes a change to one part of the module, only files dependent on the changed part need be recompiled (instead of having to recompile all files that use the module). Module Mapping Modules map to a corresponding scoping construct in each programming language. (For example, for C++ and C#, Slice modules map to namespaces whereas, for Java, they map to packages.) This allows you to use an appropriate C++ or Java declaration tousing import avoid excessively long identifiers in your source code. The Ice Module APIs for the Ice run time, apart from a small number of language-specific calls that cannot be expressed in Ice, are defined in the Ice module. In other words, most of the Ice API is actually expressed as Slice definitions. The advantage of doing this is that a single Slice definition is sufficient to define the API for the Ice run time for all supported languages. The respective language mapping rules then determine the exact shape of each Ice API for each implementation language. We will incrementally explore the contents of the module throughout this manual.Ice See Also Slice Source FilesIce 3.4.2 Documentation 83 Copyright © 2011, ZeroC, Inc. Basic Types On this page: Built-In Basic Types Integer Types Floating-Point Types Strings Booleans Bytes Built-In Basic Types Slice provides a number of built-in basic types, as shown in this table: Type Range of Mapped Type Size of Mapped Type bool or false true ? 1bit byte -128-127 or 0-255 a ? 8 bits short -2 to 2 -115 15 ? 16 bits int -2 to 2 -131 31 ? 32 bits long -2 to 2 -163 63 ? 64 bits float IEEE single-precision ? 32 bits double IEEE double-precision ? 64 bits string All Unicode characters, excluding the character with all bits zero. Variable-length a The range depends on whether maps to a signed or an unsigned type.byte All the basic types (except ) are subject to changes in representation as they are transmitted between clients and servers. For example,byte a value is byte-swapped when sent from a little-endian to a big-endian machine. Similarly, strings undergo translation in representationlong if they are sent, for example, from an EBCDIC to an ASCII implementation, and the characters of a string may also change in size. (Not all architectures use 8-bit characters). However, these changes are transparent to the programmer and do exactly what is required. Integer Types Slice provides integer types , , and , with 16-bit, 32-bit, and 64-bit ranges, respectively. Note that, on some architectures,short int long any of these types may be mapped to a native type that is wider. Also note that no unsigned types are provided. (This choice was made because unsigned types are difficult to map into languages without native unsigned types, such as Java. In addition, the unsigned integers add little value to a language. (See for a good treatment of the topic.)[1] Floating-Point Types These types follow the IEEE specification for single- and double-precision floating-point representation . If an implementation cannot[2] support IEEE format floating-point values, the Ice run time converts values into the native floating-point representation (possibly at a loss of precision or even magnitude, depending on the capabilities of the native floating-point format). Strings Slice strings use the Unicode character set. The only character that cannot appear inside a string is the zero character.Ice 3.4.2 Documentation 84 Copyright © 2011, ZeroC, Inc. 1. 2. This decision was made as a concession to C++, with which it becomes impossibly difficult to manipulate strings with embedded zero characters using standard library routines, such as or .strlen strcat The Slice data model does not have the concept of a null string (in the sense of a C++ null pointer). This decision was made because null strings are difficult to map to languages without direct support for this concept (such as Python). Do not design interfaces that depend on a null string to indicate "not there" semantics. If you need the notion of an optional string, use a , a of strings, or use an emptyclass sequence string to represent the idea of a null string. (Of course, the latter assumes that the empty string is not otherwise used as a legitimate string value by your application.) Booleans Boolean values can have only the values and . Language mappings use the corresponding native boolean type if one isfalse true available. Bytes The Slice type is an (at least) 8-bit type that is guaranteed not to undergo any changes in representation as it is transmitted betweenbyte address spaces. This guarantee permits exchange of binary data such that it is not tampered with in transit. All other Slice types are subject to changes in representation during transmission. See Also Sequences Classes References Lakos, J. 1996. . Reading, MA: Addison-Wesley.Large-Scale C++ Software Design Institute of Electrical and Electronics Engineers. 1985. . Piscataway,IEEE 754-1985 Standard for Binary Floating-Point Arithmetic NJ: Institute of Electrical and Electronic Engineers.Ice 3.4.2 Documentation 85 Copyright © 2011, ZeroC, Inc. User-Defined Types In addition to providing the built-in basic types, Slice allows you to define complex types: enumerations, structures, sequences, and dictionaries. Topics Enumerations Structures Sequences Dictionaries Constants and LiteralsIce 3.4.2 Documentation 86 Copyright © 2011, ZeroC, Inc. Enumerations A Slice enumerated type definition looks like the C++ version: Slice enum Fruit { Apple, Pear, Orange }; This definition introduces a type named that becomes a new type in its own right. Slice does not define how ordinal values areFruit assigned to enumerators. For example, you cannot assume that the enumerator will have the value 2 in different implementationOrange languages. Slice guarantees only that the ordinal values of enumerators increase from left to right, so compares less than in allApple Pear implementation languages. Unlike C++, Slice does not permit you to control the ordinal values of enumerators (because many implementation languages do not support such a feature): Slice enum Fruit { Apple = 0, Pear = 7, Orange = 2 }; // Syntax error In practice, you do not care about the values used for enumerators as long as you do not transmit the of an enumeratorordinal value between address spaces. For example, sending the value 0 to a server to mean can cause problems because the server may notApple use 0 to represent . Instead, simply send the value itself. If is represented by a different ordinal value in the receivingApple Apple Apple address space, that value will be appropriately translated by the Ice run time. As with C++, Slice enumerators enter the enclosing namespace, so the following is illegal: Slice enum Fruit { Apple, Pear, Orange }; enum ComputerBrands { Apple, IBM, Sun, HP }; // Apple redefined Slice does not permit empty enumerations. See Also Structures Sequences Dictionaries Constants and LiteralsIce 3.4.2 Documentation 87 Copyright © 2011, ZeroC, Inc. Structures Slice supports structures containing one or more named members of arbitrary type, including user-defined complex types. For example: Slice struct TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; As in C++, this definition introduces a new type called . Structure definitions form a namespace, so the names of the structureTimeOfDay members need to be unique only within their enclosing structure. Data member definitions using a named type are the only construct that can appear inside a structure. It is impossible to, for example, define a structure inside a structure: Slice struct TwoPoints { struct Point { // Illegal! short x; short y; }; Point coord1; Point coord2; }; This rule applies to Slice in general: type definitions cannot be nested (except for , which do support nesting). The reason for thismodules rule is that nested type definitions can be difficult to implement for some target languages and, even if implementable, greatly complicate the scope resolution rules. For a specification language, such as Slice, nested type definitions are unnecessary – you can always write the above definitions as follows (which is stylistically cleaner as well): Slice struct Point { short x; short y; }; struct TwoPoints { // Legal (and cleaner!) Point coord1; Point coord2; }; You can specify a default value for a data member that has one of the following types: An type ( , , , )integral byte short int long A type ( or )floating point float double string bool enum For example:Ice 3.4.2 Documentation 88 Copyright © 2011, ZeroC, Inc. Slice struct Location { string name; Point pt; bool display = true; string source = "GPS"; }; The legal syntax for literal values is the same as for Slice , and you may also use a constant as a default value. The languageconstants mapping guarantees that data members are initialized to their declared default values using a language-specific mechanism. See Also Modules Basic Types Enumerations Sequences Dictionaries Constants and LiteralsIce 3.4.2 Documentation 89 Copyright © 2011, ZeroC, Inc. Sequences On this page: Sequence Syntax and Semantics Using Sequences for Optional Values Sequence Syntax and Semantics Sequences are variable-length collections of elements: Slice sequence FruitPlatter; A sequence can be empty?—?that is, it can contain no elements, or it can hold any number of elements up to the memory limits of your platform. Sequences can contain elements that are themselves sequences. This arrangement allows you to create lists of lists: Slice sequence FruitBanquet; Sequences are used to model a variety of collections, such as vectors, lists, queues, sets, bags, or trees. (It is up to the application to decide whether or not order is important; by discarding order, a sequence serves as a set or bag.) Using Sequences for Optional Values One particular use of sequences has become idiomatic, namely, the use of a sequence to indicate an optional value. For example, we might have a structure that records the details of the parts that go into a car. The structure could record things such as the name of the part,Part a description, weight, price, and other details. Spare parts commonly have a serial number, which we can model as a value. However,long some parts, such as simple screws, often do not have a serial number, so what are we supposed to put into the serial number field of a screw? There are a number of options for dealing with this situation: Use a sentinel value, such as zero, to indicate the "no serial number" condition. This approach is workable, provided that a sentinel value is actually available. While it may seem unlikely that anyone would use a serial number of zero for a part, it is not impossible. And, for other values, such as a temperature value, all values in the range of their type can be legal, so no sentinel value is available. Change the type of the serial number from to .long string Strings come with their own built-in sentinel value, namely the empty string, so we can use an empty string to indicate the "no serial number" case. This is workable but not ideal: we should not have to change the natural data type of something to just so westring get a sentinel value. Add an indicator as to whether the contents of the serial number are valid: Slice struct Part { string name; string description; // ... bool serialIsValid; // true if part has serial number long serialNumber; }; This is guaranteed to get you into trouble eventually: sooner or later, some programmer will forget to check whether the serial number is valid before using it and create havoc. Use a sequence to model the optional field.Ice 3.4.2 Documentation 90 Copyright © 2011, ZeroC, Inc. This technique uses the following convention: Slice sequence SerialOpt; struct Part { string name; string description; // ... SerialOpt serialNumber; // optional: zero or one element }; By convention, the suffix is used to indicate that the sequence is used to model an optional value. If the sequence is empty, theOpt value is obviously not there; if it contains a single element, that element is the value. The obvious drawback of this scheme is that someone could put more than one element into the sequence. This could be rectified by adding a special-purpose Slice construct for optional values. However, optional values are not used frequently enough to justify the complexity of adding a dedicated language feature. (As we will see in , you can also use class hierarchies to model optional fields.)Classes See Also Enumerations Structures Dictionaries Constants and Literals ClassesIce 3.4.2 Documentation 91 Copyright © 2011, ZeroC, Inc. Dictionaries On this page: Dictionary Syntax and Semantics Allowable Types for Dictionary Keys and Values Dictionary Syntax and Semantics A dictionary is a mapping from a key type to a value type. For example: Slice struct Employee { long number; string firstName; string lastName; }; dictionary EmployeeMap; This definition creates a dictionary named that maps from an employee number to a structure containing the details for anEmployeeMap employee. Whether or not the key type (the employee number, of type in this example) is also part of the value type (the long Employee structure in this example) is up to you — as far as Slice is concerned, there is no need to include the key as part of the value. Dictionaries can be used to implement sparse arrays, or any lookup data structure with non-integral key type. Even though a sequence of structures containing key-value pairs could be used to model the same thing, a dictionary is more appropriate: A dictionary clearly signals the intent of the designer, namely, to provide a mapping from a domain of values to a range of values. (A sequence of structures of key-value pairs does not signal that same intent as clearly.) At the programming language level, sequences are implemented as vectors (or possibly lists), that is, they are not well suited to model sparsely populated domains and require a linear search to locate an element with a particular value. On the other hand, dictionaries are implemented as a data structure (typically a hash table or red-black tree) that supports efficient searching in (log )O n average time or better. Allowable Types for Dictionary Keys and Values The key type of a dictionary need not be an integral type. For example, we could use the following definition to translate the names of the days of the week: Slice dictionary WeekdaysEnglishToGerman; The server implementation would take care of initializing this map with the key-value pairs , , and soMonday-Montag Tuesday-Dienstag on. The value type of a dictionary can be any Slice type. However, the key type of a dictionary is limited to one of the following types: Integral types ( , , , , )byte short int long bool string enum Structures containing only data members of integral type or string Complex nested types, such as nested structures, sequences, or dictionaries, and floating-point types ( and ) cannot be usedfloat double as the key type. Complex nested types are disallowed because they complicate the language mappings for dictionaries, and floating-point types are disallowed because representational changes of values as they cross machine boundaries can lead to ill-defined semantics for equality.Ice 3.4.2 Documentation 92 Copyright © 2011, ZeroC, Inc. See Also Basic Types Enumerations Structures Sequences Constants and LiteralsIce 3.4.2 Documentation 93 Copyright © 2011, ZeroC, Inc. Constants and Literals On this page: Allowable Types for Constants Boolean constants Integer literals Floating-point literals String literals Allowable Types for Constants Slice allows you to define constants for the following types: An type ( , , , , )integral bool byte short int long A type ( or )floating point float double string enum Here are a few examples: Slice const bool AppendByDefault = true; const byte LowerNibble = 0x0f; const string Advice = "Don't Panic!"; const short TheAnswer = 42; const double PI = 3.1416; enum Fruit { Apple, Pear, Orange }; const Fruit FavoriteFruit = Pear; The syntax for literals is the same as for C++ and Java (with a few minor exceptions). Boolean constants Boolean constants can only be initialized with the keywords and . (You cannot use and to represent and .)false true 0 1 false true Integer literals Integer literals can be specified in decimal, octal, or hexadecimal notation. For example: Slice const byte TheAnswer = 42; const byte TheAnswerInOctal = 052; const byte TheAnswerInHex = 0x2A; // or 0x2a Be aware that, if you interpret as a number instead of a bit pattern, you may get different results in different languages. For example,byte for C++, maps to whereas, for Java, maps to , which is a signed type.byte unsigned char byte byte Note that suffixes to indicate long and unsigned constants ( , , , , used by C++) are illegal:l L u U Slice const long Wrong = 0u; // Syntax error const long WrongToo = 1000000L; // Syntax errorIce 3.4.2 Documentation 94 Copyright © 2011, ZeroC, Inc. The value of an integer literal must be within the range of its constant type, as shown in the ; otherwise theBuilt-In Basic Types table compiler will issue a diagnostic. Floating-point literals Floating-point literals use C++ syntax, except that you cannot use an or suffix to indicate an extended floating-point constant; however, l L f and are legal (but are ignored).F Here are a few examples: Slice const float P1 = -3.14f; // Integer & fraction, with suffix const float P2 = +3.1e-3; // Integer, fraction, and exponent const float P3 = .1; // Fraction part only const float P4 = 1.; // Integer part only const float P5 = .9E5; // Fraction part and exponent const float P6 = 5e2; // Integer part and exponent Floating-point literals must be within the range of the constant type ( or ); otherwise, the compiler will issue a diagnostic.float double String literals String literals support the same escape sequences as C++. Here are some examples: Slice const string AnOrdinaryString = "Hello World!"; const string DoubleQuote = "\""; const string TwoSingleQuotes = "'\'"; // ' and \' are OK const string Newline = "\n"; const string CarriageReturn = "\r"; const string HorizontalTab = "\t"; const string VerticalTab = "\v"; const string FormFeed = "\f"; const string Alert = "\a"; const string Backspace = "\b"; const string QuestionMark = "\?"; const string Backslash = "\\"; const string OctalEscape = "\007"; // Same as \a const string HexEscape = "\x07"; // Ditto Note that Slice has no concept of a null string: Slice const string nullString = 0; // Illegal! Null strings simply do not exist in Slice and, therefore, do not exist as a legal value for a string anywhere in the Ice platform. The reason for this decision is that null strings do not exist in many programming languages. Many languages other than C and C++ use a byte array as the internal string representation. Null strings do not exist (and would be very difficult to map) in such languages. A constant definition may also refer to another constant. It is not necessary for both constants to have the same Slice type, but the value of the existing constant must be compatible with the type of the constant being defined.Ice 3.4.2 Documentation 95 Copyright © 2011, ZeroC, Inc. Consider the examples below: Slice const int SIZE = 500; const int DEFAULT_SIZE = SIZE; // OK const short SHORT_SIZE = SIZE; // OK const byte BYTE_SIZE = SIZE; // ERROR The constant is legal because it has the same type as , and is legal because the value of ( ) isDEFAULT_SIZE SIZE SHORT_SIZE SIZE 500 within the range of the Slice type. However, is illegal because the value of is outside the range of the type.short BYTE_SIZE SIZE byte See Also Enumerations Structures Sequences DictionariesIce 3.4.2 Documentation 96 Copyright © 2011, ZeroC, Inc. Interfaces, Operations, and Exceptions The central focus of Slice is on defining interfaces, for example: Slice struct TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; interface Clock { TimeOfDay getTime(); void setTime(TimeOfDay time); }; This definition defines an interface type called . The interface supports two operations: and . Clients access anClock getTime setTime object supporting the interface by invoking an operation on the proxy for the object: to read the current time, the client invokes the Clock operation; to set the current time, the client invokes the operation, passing an argument of type .getTime setTime TimeOfDay Invoking an operation on a proxy instructs the Ice run time to send a message to the target object. The target object can be in another address space or can be collocated (in the same process) as the caller — the location of the target object is transparent to the client. If the target object is in another (possibly remote) address space, the Ice run time invokes the operation via a remote procedure call; if the target is collocated with the client, the Ice run time uses an ordinary function call instead, to avoid the overhead of marshaling. You can think of an interface definition as the equivalent of the public part of a C++ class definition or as the equivalent of a Java interface, and of operation definitions as (virtual) member functions. Note that nothing but operation definitions are allowed to appear inside an interface definition. In particular, you cannot define a type, an exception, or a data member inside an interface. This does not mean that your object implementation cannot contain state — it can, but how that state is implemented (in the form of data members or otherwise) is hidden from the client and, therefore, need not appear in the object's interface definition. An Ice object has exactly one (most derived) Slice interface type (or ). Of course, you can create multiple Ice objects that have theclass type same type; to draw the analogy with C++, a Slice interface corresponds to a C++ class definition, whereas an Ice object corresponds to a C++ class instance (but Ice objects can be implemented in multiple different address spaces). Ice also provides multiple interfaces via a feature called .facets A Slice interface defines the smallest grain of distribution in Ice: each Ice object has a unique identity (encapsulated in its proxy) that distinguishes it from all other Ice objects; for communication to take place, you must invoke operations on an object's proxy. There is no other notion of an addressable entity in Ice. You cannot, for example, instantiate a Slice structure and have clients manipulate that structure remotely. To make the structure accessible, you must create an interface that allows clients to access the structure. The partition of an application into interfaces therefore has profound influence on the overall architecture. Distribution boundaries must follow interface (or class) boundaries; you can spread the implementation of interfaces over multiple address spaces (and you can implement multiple interfaces in the same address space), but you cannot implement parts of interfaces in different address spaces. Topics Operations User Exceptions Run-Time Exceptions Proxies Interface Inheritance See Also Classes Facets and VersioningIce 3.4.2 Documentation 97 Copyright © 2011, ZeroC, Inc. Operations On this page: Parameters and Return Values Style of Operation Definition Overloading Operations Idempotent Operations Parameters and Return Values An operation definition must contain a return type and zero or more parameter definitions. For example, in the interface, the Clock getTime operation has a return type of and the operation has a return type of . You must use to indicate that anTimeOfDay setTime void void operation returns no value — there is no default return type for Slice operations. An operation can have one or more input parameters. For example, accepts a single input parameter of type called setTime TimeOfDay . Of course, you can use multiple input parameters:time Slice interface CircadianRhythm { void setSleepPeriod(TimeOfDay startTime, TimeOfDay stopTime); // ... }; Note that the parameter name (as for Java) is mandatory. You cannot omit the parameter name, so the following is in error: Slice interface CircadianRhythm { void setSleepPeriod(TimeOfDay, TimeOfDay); // Error! // ... }; By default, parameters are sent from the client to the server, that is, they are input parameters. To pass a value from the server to the client, you can use an output parameter, indicated by the keyword. For example, an alternative way to define the operation in the out getTime interface would be:Clock Slice void getTime(out TimeOfDay time); This achieves the same thing but uses an output parameter instead of the return value. As with input parameters, you can use multiple output parameters: Slice interface CircadianRhythm { void setSleepPeriod(TimeOfDay startTime, TimeOfDay stopTime); void getSleepPeriod(out TimeOfDay startTime, out TimeOfDay stopTime); // ... }; If you have both input and output parameters for an operation, the output parameters must follow the input parameters:Ice 3.4.2 Documentation 98 Copyright © 2011, ZeroC, Inc. Slice void changeSleepPeriod( TimeOfDay startTime, TimeOfDay stopTime, // OK out TimeOfDay prevStartTime, out TimeOfDay prevStopTime); void changeSleepPeriod(out TimeOfDay prevStartTime, out TimeOfDay prevStopTime, // Error TimeOfDay startTime, TimeOfDay stopTime); Slice does not support parameters that are both input and output parameters (call by reference). The reason is that, for remote calls, reference parameters do not result in the same savings that one can obtain for call by reference in programming languages. (Data still needs to be copied in both directions and any gains in marshaling efficiency are negligible.) Also, reference (or input-output) parameters result in more complex language mappings, with concomitant increases in code size. Style of Operation Definition As you would expect, language mappings follow the style of operation definition you use in Slice: Slice return types map to programming language return types, and Slice parameters map to programming language parameters. For operations that return only a single value, it is common to return the value from the operation instead of using an out-parameter. This style maps naturally into all programming languages. Note that, if you use an out-parameter instead, you impose a different API style on the client: most programming languages permit the return value of a function to be ignored whereas it is typically not possible to ignore an output parameter. For operations that return multiple values, it is common to return all values as out-parameters and to use a return type of . However, thevoid rule is not all that clear-cut because operations with multiple output values can have one particular value that is considered more "important" than the remainder. A common example of this is an iterator operation that returns items from a collection one-by-one: Slice bool next(out RecordType r); The operation returns two values: the record that was retrieved and a Boolean to indicate the end-of-collection condition. (If the returnnext value is , the end of the collection has been reached and the parameter has an undefined value.) This style of definition can befalse r useful because it naturally fits into the way programmers write control structures. For example: while (next(record)) // Process record... if (next(record)) // Got a valid record... Overloading Operations Slice does not support any form of overloading of operations. For example: Slice interface CircadianRhythm { void modify(TimeOfDay startTime, TimeOfDay endTime); void modify( TimeOfDay startTime, // Error TimeOfDay endTime, out timeOfDay prevStartTime, out TimeOfDay prevEndTime); }; Operations in the same interface must have different names, regardless of what type and number of parameters they have. This restriction exists because overloaded functions cannot sensibly be mapped to languages without built-in support for overloading.Ice 3.4.2 Documentation 99 Copyright © 2011, ZeroC, Inc. Name mangling is not an option in this case: while it works fine for compilers, it is unacceptable to humans. Idempotent Operations Some operations, such as in the interface, do not modify the state of the object they operate on. They are the conceptualgetTime Clock equivalent of C++ member functions. Similary, does modify the state of the object, but is idempotent. You can indicate thisconst setTime in Slice as follows: Slice interface Clock { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time); }; This marks the and operations as idempotent. An operation is idempotent if two successive invocations of the operationgetTime setTime have the same effect as a single invocation. For example, is an idempotent operation because it does not matter whether it isx = 1; executed once or twice — either way, ends up with the value 1. On the other hand, is not an idempotent operation becausex x += 1; executing it twice results in a different value for than executing it once. Obviously, any read-only operation is idempotent.x The keyword is useful because it allows the Ice run time to be more aggressive when performing to recoveridempotent automatic retries from errors. Specifically, Ice guarantees semantics for operation invocations:at-most-once For normal (not idempotent) operations, the Ice run time has to be conservative about how it deals with errors. For example, if a client sends an operation invocation to a server and then loses connectivity, there is no way for the client-side run time to find out whether the request it sent actually made it to the server. This means that the run time cannot attempt to recover from the error by re-establishing a connection and sending the request a second time because that could cause the operation to be invoked a second time and violate at-most-once semantics; the run time has no option but to report the error to the application. For operations, on the other hand, the client-side run time can attempt to re-establish a connection to the server andidempotent safely send the failed request a second time. If the server can be reached on the second attempt, everything is fine and the application never notices the (temporary) failure. Only if the second attempt fails need the run time report the error back to the application. (The number of retries can be increased with an Ice configuration parameter.) See Also Interfaces, Operations, and Exceptions User Exceptions Run-Time Exceptions Proxies Interface Inheritance Automatic RetriesIce 3.4.2 Documentation 100 Copyright © 2011, ZeroC, Inc. User Exceptions On this page: User Exception Syntax and Semantics Default Values for User Exception Members Declaring User Exceptions in Operations Restrictions for User Exceptions User Exception Inheritance User Exception Syntax and Semantics Looking at the operation in the interface, we find a potential problem: given that the structure uses assetTime Clock TimeOfDay short the type of each field, what will happen if a client invokes the operation and passes a value with meaningless fieldsetTime TimeOfDay values, such as for the minute field, or for the hour? Obviously, it would be nice to provide some indication to the caller that this is-199 42 meaningless. Slice allows you to define user exceptions to indicate error conditions to the client. For example: Slice exception Error {}; // Empty exceptions are legal exception RangeError { TimeOfDay errorTime; TimeOfDay minTime; TimeOfDay maxTime; }; A user exception is much like a structure in that it contains a number of data members. However, unlike structures, exceptions can have zero data members, that is, be empty. Default Values for User Exception Members You can specify a default value for an exception data member that has one of the following types: An type ( , , , )integral byte short int long A type ( or )floating point float double string bool enum For example: Slice exception RangeError { TimeOfDay errorTime; TimeOfDay minTime; TimeOfDay maxTime; string reason = "out of range"; }; The legal syntax for literal values is the same as for , and you may also use a constant as a default value. The languageSlice constants mapping guarantees that data members are initialized to their declared default values using a language-specific mechanism. Declaring User Exceptions in Operations Exceptions allow you to return an arbitrary amount of error information to the client if an error condition arises in the implementation of an operation. Operations use an exception specification to indicate the exceptions that may be returned to the client:Ice 3.4.2 Documentation 101 Copyright © 2011, ZeroC, Inc. Slice interface Clock { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time) throws RangeError, Error; }; This definition indicates that the operation may throw either a or an user exception (and no other type ofsetTime RangeError Error exception). If the client receives a exception, the exception contains the value that was passed to andRangeError TimeOfDay setTime caused the error (in the member), as well as the minimum and maximum time values that can be used (in the and errorTime minTime members). If failed because of an error not caused by an illegal parameter value, it throws . Obviously, because maxTime setTime Error does not have data members, the client will have no idea what exactly it was that went wrong — it simply knows that the operationError did not work. An operation can throw only those user exceptions that are listed in its exception specification. If, at run time, the implementation of an operation throws an exception that is not listed in its exception specification, the client receives a ) to indicate that therun-time exception operation did something illegal. To indicate that an operation does not throw any user exception, simply omit the exception specification. (There is no empty exception specification in Slice.) Restrictions for User Exceptions Exceptions are not first-class data types and first-class data types are not exceptions: You cannot pass an exception as a parameter value. You cannot use an exception as the type of a data member. You cannot use an exception as the element type of a sequence. You cannot use an exception as the key or value type of a dictionary. You cannot throw a value of non-exception type (such as a value of type or ).int string The reason for these restrictions is that some implementation languages use a specific and separate type for exceptions (in the same way as Slice does). For such languages, it would be difficult to map exceptions if they could be used as an ordinary data type. (C++ is somewhat unusual among programming languages by allowing arbitrary types to be used as exceptions.) User Exception Inheritance Exceptions support inheritance. For example: Slice exception ErrorBase { string reason; }; enum RTError { DivideByZero, NegativeRoot, IllegalNull /* ... */ }; exception RuntimeError extends ErrorBase { RTError err; }; enum LError { ValueOutOfRange, ValuesInconsistent, /* ... */ }; exception LogicError extends ErrorBase { LError err; }; exception RangeError extends LogicError { TimeOfDay errorTime; TimeOfDay minTime; TimeOfDay maxTime; };Ice 3.4.2 Documentation 102 Copyright © 2011, ZeroC, Inc. These definitions set up a simple exception hierarchy: ErrorBase is at the root of the tree and contains a string explaining the cause of the error. Derived from are and . Each of these exceptions contains an enumerated value thatErrorBase RuntimeError LogicError further categorizes the error. Finally, is derived from and reports the details of the specific error.RangeError LogicError Setting up exception hierarchies such as this not only helps to create a more readable specification because errors are categorized, but also can be used at the language level to good advantage. For example, the Slice C++ mapping preserves the exception hierarchy so you can catch exceptions generically as a base exception, or set up exception handlers to deal with specific exceptions. Looking at the exception hierarchy, it is not clear whether, at run time, the application will only throw most derived exceptions, such as , or if it will also throw base exceptions, such as , , and . If you want to indicate that aRangeError LogicError RuntimeError ErrorBase base exception, interface, or class is abstract (will not be instantiated), you can add a comment to that effect. Note that, if the exception specification of an operation indicates a specific exception type, at run time, the implementation of the operation may also throw more derived exceptions. For example: Slice exception Base { // ... }; exception Derived extends Base { // ... }; interface Example { void op() throws Base; // May throw Base or Derived }; In this example, may throw a or a exception, that is, any exception that is compatible with the exception types listed in theop Base Derived exception specification can be thrown at run time. As a system evolves, it is quite common for new, derived exceptions to be added to an existing hierarchy. Assume that we initially construct clients and server with the following definitions: Slice exception Error { // ... }; interface Application { void doSomething() throws Error; }; Also assume that a large number of clients are deployed in field, that is, when you upgrade the system, you cannot easily upgrade all the clients. As the application evolves, a new exception is added to the system and the server is redeployed with the new definition: Slice exception Error { // ... }; exception FatalApplicationError extends Error { // ... }; interface Application { void doSomething() throws Error; };Ice 3.4.2 Documentation 103 Copyright © 2011, ZeroC, Inc. This raises the question of what should happen if the server throws a from . The answerFatalApplicationError doSomething depends whether the client was built using the old or the updated definition: If the client was built using the same definition as the server, it simply receives a .FatalApplicationError If the client was built with the original definition, that client has no knowledge that even exists. In thisFatalApplicationError case, the Ice run time automatically slices the exception to the most-derived type that is understood by the receiver ( , in thisError case) and discards the information that is specific to the derived part of the exception. (This is exactly analogous to catching C++ exceptions by value — the exception is sliced to the type used in the -clause.)catch Exceptions support single inheritance only. (Multiple inheritance would be difficult to map into many programming languages.) See Also Constants and Literals Operations Run-Time Exceptions Proxies Interface InheritanceIce 3.4.2 Documentation 104 Copyright © 2011, ZeroC, Inc. Run-Time Exceptions In addition to any that are listed in an operation's exception specification, an operation can also throw Ice user exceptions run-time . Run-time exceptions are predefined exceptions that indicate platform-related run-time errors. For example, if a networking errorexceptions interrupts communication between client and server, the client is informed of this by a run-time exception, such as or .ConnectTimeoutException SocketException The exception specification of an operation must not list any run-time exceptions. (It is understood that all operations can raise run-time exceptions and you are not allowed to restate that.) On this page: Inheritance Hierarchy for Exceptions Local Versus Remote Exceptions Common Exceptions ObjectNotExistException FacetNotExistException OperationNotExistException Unknown Exceptions UnknownUserException UnknownLocalException UnknownException Inheritance Hierarchy for Exceptions All the Ice run-time and user exceptions are arranged in an inheritance hierarchy, as shown below: Inheritance structure for exceptions. Ice::Exception is at the root of the inheritance hierarchy. Derived from that are the (abstract) types and Ice::LocalException . In turn, all run-time exceptions are derived from , and all user exceptions are derivedIce::UserException Ice::LocalException from .Ice::UserException This figures shows the complete hierarchy of the Ice run-time exceptions:Ice 3.4.2 Documentation 105 Copyright © 2011, ZeroC, Inc. Ice run-time exception hierarchy. (Shaded exceptions can be sent by the server.) We use the Unified Modeling Language (UML) for the object model diagrams (see and for details).[1] [2] Note that groups several exceptions into a single box to save space (which, strictly, is incorrect UMLIce run-time exception hierarchy syntax). Also note that some run-time exceptions have data members, which, for brevity, we have omitted in the Ice run-time exception . These data members provide additional information about the precise cause of an error.hierarchy Many of the run-time exceptions have self-explanatory names, such as . Others indicate problems in the Ice runMemoryLimitExceptionIce 3.4.2 Documentation 106 Copyright © 2011, ZeroC, Inc. time, such as . Still others can arise only through application programming errors, such as EncapsulationException . In practice, you will likely never see most of these exceptions. However, there are a few run-time exceptions youTwowayOnlyException will encounter and whose meaning you should know. Local Versus Remote Exceptions Common Exceptions Most error conditions are detected on the client side. For example, if an attempt to contact a server fails, the client-side run time raises a . However, there are three specific error conditions (shown as shaded in the ConnectTimeoutException Ice run-time exception hierarchy diagram) that are detected by the server and made known explicitly to the client-side run time via the Ice protocol: , , and .ObjectNotExistException FacetNotExistException OperationNotExistException ObjectNotExistException This exception indicates that a request was delivered to the server but the server could not locate a servant with the identity that is embedded in the proxy. In other words, the server could not find an object to dispatch the request to. An is a death certificate: it indicates that the target object in the server does not exist.ObjectNotExistException The Ice run time raises only if there are no in existence with a matching identity;ObjectNotExistException facets otherwise, it raises .FacetNotExistException Most likely, this is the case because the object existed some time in the past and has since been destroyed, but the same exception is also raised if a client uses a proxy with the identity of an object that has never been created. If you receive this exception, you are expected to clean up whatever resources you might have allocated that relate to the specific object for which you receive this exception. FacetNotExistException The client attempted to contact a non-existent of an object, that is, the server has at least one servant with the given identity, but nofacets servant with a matching facet name. OperationNotExistException This exception is raised if the server could locate an object with the correct identity but, on attempting to dispatch the client's operation invocation, the server found that the target object does not have such an operation. You will see this exception in only two cases: You have used an unchecked down-cast on a proxy of the incorrect type. Client and server have been built with Slice definitions for an interface that disagree with each other, that is, the client was built with an interface definition for the object that indicates that an operation exists, but the server was built with a different version of the interface definition in which the operation is absent. Unknown Exceptions Any error condition on the server side that is not described by one of the three preceding exceptions is made known to the client as one of three generic exceptions (shown as shaded in the diagram): , Ice run-time exception hierarchy figure UnknownUserException , or .UnknownLocalException UnknownException UnknownUserException This exception indicates that an operation implementation has thrown a Slice exception that is not declared in the operation's exception specification (and is not derived from one of the exceptions in the operation's exception specification). UnknownLocalException If an operation implementation raises a run-time exception other than , , or ObjectNotExistException FacetNotExistException (such as a ), the client receives an . In otherOperationNotExistException NotRegisteredException UnknownLocalException words, the Ice protocol does not transmit the exact exception that was encountered in the server, but simply returns a bit to the client in the reply to indicate that the server encountered a run-time exception. A common cause for a client receiving an is failure to catch and handle all exceptions in the server. ForUnknownLocalException example, if the implementation of an operation encounters an exception it does not handle, the exception propagates all the way up the call stack until the stack is unwound to the point where the Ice run time invoked the operation. The Ice run time catches all Ice exceptions thatIce 3.4.2 Documentation 107 Copyright © 2011, ZeroC, Inc. 1. 2. "escape" from an operation invocation and returns them to the client as an .UnknownLocalException UnknownException An operation has thrown a non-Ice exception. For example, if the operation in the server throws a C++ exception, such as a , or achar* Java exception, such as a , the client receives an .ClassCastException UnknownException All other run-time exceptions (not shaded in the ) are detected by the client-side run time and are raisedIce run-time exception hierarchy locally. It is possible for the implementation of an operation to throw Ice run-time exceptions (as well as user exceptions). For example, if a client holds a proxy to an object that no longer exists in the server, your server application code is required to throw an . If you do throw run-time exceptions from your application code, you should take care to throw a run-timeObjectNotExistException exception only if appropriate, that is, do not use run-time exceptions to indicate something that really should be a user exception. Doing so can be very confusing to the client: if the application "hijacks" some run-time exceptions for its own purposes, the client can no longer decide whether the exception was thrown by the Ice run time or by the server application code. This can make debugging very difficult. See Also User Exceptions Interfaces, Operations, and Exceptions Operations Proxies Interface Inheritance Facets and Versioning References Booch, G., et al. 1998. . Reading, MA: Addison-Wesley.Unified Modeling Language User Guide Object Management Group. 2001. . Framingham, MA: Object Management Group.Unified Modeling Language SpecificationIce 3.4.2 Documentation 108 Copyright © 2011, ZeroC, Inc. Proxies Building on the example, we can create definitions for a world-time server:Clock Slice exception GenericError { string reason; }; struct TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; exception BadTimeVal extends GenericError {}; interface Clock { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time) throws BadTimeVal; }; dictionary TimeMap; // Time zone name to clock map exception BadZoneName extends GenericError {}; interface WorldTime { idempotent void addZone(string zoneName, Clock* zoneClock); void removeZone(string zoneName) throws BadZoneName; idempotent Clock* findZone(string zoneName) throws BadZoneName; idempotent TimeMap listZones(); idempotent void setZones(TimeMap zones); }; The interface acts as a collection manager for clocks, one for each time zone. In other words, the interfaceWorldTime WorldTime manages a collection of pairs. The first member of each pair is a time zone name; the second member of the pair is the clock that provides the time for that zone. The interface contains operations that permit you to add or remove a clock from the map ( and addZone removeZone ), to search for a particular time zone by name ( ), and to read or write the entire map ( and ).findZone listZones setZones The example illustrates an important Slice concept: note that accepts a parameter of type and WorldTime addZone Clock* findZone returns a parameter of type . In other words, interfaces are types in their own right and can be passed as parameters. The operatorClock* * is known as the . Its left-hand argument must be an interface (or ) and its return type is a proxy. A proxy is like a pointerproxy operator class that can denote an object. The semantics of proxies are very much like those of C++ class instance pointers: A proxy can be .null A proxy can dangle (point at an object that is no longer there). Operations dispatched via a proxy use late binding: if the actual run-time type of the object denoted by the proxy is more derived than the proxy's type, the implementation of the most-derived interface will be invoked. When a client passes a proxy to the operation, the proxy denotes an actual object in a server. The Clock addZone Clock Clock Ice object denoted by that proxy may be implemented in the same server process as the interface, or in a different server process. WhereWorldTime the object is physically implemented matters neither to the client nor to the server implementing the interface; if eitherClock WorldTime invokes an operation on a particular clock, such as , an RPC call is sent to whatever server implements that particular clock. IngetTime other words, a proxy acts as a local "ambassador" for the remote object; invoking an operation on the proxy forwards the invocation to the actual object implementation. If the object implementation is in a different address space, this results in a remote procedure call; if the object implementation is collocated in the same address space, the Ice run time uses an ordinary local function call from the proxy to the object implementation. Note that proxies also act very much like pointers in their sharing semantics: if two clients have a proxy to the same object, a state change made by one client (such as setting the time) will be visible to the other client. Proxies are strongly typed (at least for statically typed languages, such as C++ and Java). This means that you cannot pass something other than a proxy to the operation; attempts to do so are rejected at compile time.Clock addZoneIce 3.4.2 Documentation 109 Copyright © 2011, ZeroC, Inc. See Also Classes Interfaces, Operations, and Exceptions User Exceptions Run-Time Exceptions Interface InheritanceIce 3.4.2 Documentation 110 Copyright © 2011, ZeroC, Inc. Interface Inheritance On this page: Interface Inheritance Interface Inheritance Limitations Implicit Inheritance from Object Null Proxies Self-Referential Interfaces Empty Interfaces Interface Versus Implementation Inheritance Interface Inheritance Interfaces support inheritance. For example, we could extend our to support the concept of an alarm clock:world-time server Slice interface AlarmClock extends Clock { idempotent TimeOfDay getAlarmTime(); idempotent void setAlarmTime(TimeOfDay alarmTime) throws BadTimeVal; }; The semantics of this are the same as for C++ or Java: is a subtype of and an proxy can be substitutedAlarmClock Clock AlarmClock wherever a proxy is expected. Obviously, an supports the same and operations as a butClock AlarmClock getTime setTime Clock also supports the and operations.getAlarmTime setAlarmTime Multiple interface inheritance is also possible. For example, we can construct a radio alarm clock as follows: Slice interface Radio { void setFrequency(long hertz) throws GenericError; void setVolume(long dB) throws GenericError; }; enum AlarmMode { RadioAlarm, BeepAlarm }; interface RadioClock extends Radio, AlarmClock { void setMode(AlarmMode mode); AlarmMode getMode(); }; RadioClock extends both and and can therefore be passed where a , an , or a isRadio AlarmClock Radio AlarmClock Clock expected. The inheritance diagram for this definition looks as follows:Ice 3.4.2 Documentation 111 Copyright © 2011, ZeroC, Inc. Inheritance diagram for .RadioClock Interfaces that inherit from more than one base interface may share a common base interface. For example, the following definition is legal: Slice interface B { /* ... */ }; interface I1 extends B { /* ... */ }; interface I2 extends B { /* ... */ }; interface D extends I1, I2 { /* ... */ }; This definition results in the familiar diamond shape: Diamond-shaped inheritance. Interface Inheritance Limitations If an interface uses multiple inheritance, it must not inherit the same operation name from more than one base interface. For example, the following definition is illegal:Ice 3.4.2 Documentation 112 Copyright © 2011, ZeroC, Inc. Slice interface Clock { void set(TimeOfDay time); // set time }; interface Radio { void set(long hertz); // set frequency }; interface RadioClock extends Radio, Clock { // Illegal! // ... }; This definition is illegal because inherits two operations, and . The Slice compiler makes thisRadioClock set Radio::set Clock::set illegal because (unlike C++) many programming languages do not have a built-in facility for disambiguating the different operations. In Slice, the simple rule is that all inherited operations must have unique names. (In practice, this is rarely a problem because inheritance is rarely added to an interface hierarchy "after the fact". To avoid accidental clashes, we suggest that you use descriptive operation names, such as and . This makes accidental name clashes less likely.)setTime setFrequency Implicit Inheritance from Object All Slice interfaces are ultimately derived from . For example, the would be shown more correctly as:Object inheritance hierarchy Implicit inheritance from .Object Because all interfaces have a common base interface, we can pass any type of interface as that type. For example: Slice interface ProxyStore { idempotent void putProxy(string name, Object* o); idempotent Object* getProxy(string name); }; Object is a Slice keyword (note the capitalization) that denotes the root type of the inheritance hierarchy. The interface is aProxyStoreIce 3.4.2 Documentation 113 Copyright © 2011, ZeroC, Inc. generic proxy storage facility: the client can call to add a proxy of any type under a given name and later retrieve that proxy againputProxy by calling and supplying that name. The ability to generically store proxies in this fashion allows us to build general-purposegetProxy facilities, such as a that can store proxies and deliver them to clients. Such a service, in turn, allows us to avoid hard-codingnaming service proxy details into clients and servers. Inheritance from type is always implicit. For example, the following Slice definition is illegal:Object Slice interface MyInterface extends Object { /* ... */ }; // Error! It is understood that all interfaces inherit from type ; you are not allowed to restate that.Object Type is mapped to an abstract type by the various language mappings, so you cannot instantiate an Ice object of that type.Object Null Proxies Looking at the interface once more, we notice that does not have an exception specification. The question then isProxyStore getProxy what should happen if a client calls with a name under which no proxy is stored? Obviously, we could add an exception togetProxy indicate this condition to . However, another option is to return a . Ice has the built-in notion of a null proxy, which is agetProxy null proxy proxy that "points nowhere". When such a proxy is returned to the client, the client can test the value of the returned proxy to check whether it is null or denotes a valid object. A more interesting question is: "which approach is more appropriate, throwing an exception or returning a null proxy?" The answer depends on the expected usage pattern of an interface. For example, if, in normal operation, you do not expect clients to call with agetProxy non-existent name, it is better to throw an exception. (This is probably the case for our interface: the fact that there is no ProxyStore list operation makes it clear that clients are expected to know which names are in use.) On the other hand, if you expect that clients will occasionally try to look up something that is not there, it is better to return a null proxy. The reason is that throwing an exception breaks the normal flow of control in the client and requires special handling code. This means that you should throw exceptions only in exceptional circumstances. For example, throwing an exception if a database lookup returns an empty result set is wrong; it is expected and normal that a result set is occasionally empty. It is worth paying attention to such design issues: well-designed interfaces that get these details right are easier to use and easier to understand. Not only do such interfaces make life easier for client developers, they also make it less likely that latent bugs cause problems later. Self-Referential Interfaces Proxies have pointer semantics, so we can define self-referential interfaces. For example: Slice interface Link { idempotent SomeType getValue(); idempotent Link* next(); }; The interface contains a operation that returns a proxy to a interface. Obviously, this can be used to create a chain ofLink next Link interfaces; the final link in the chain returns a null proxy from its operation.next Empty Interfaces The following Slice definition is legal: Slice interface Empty {}; The Slice compiler will compile this definition without complaint. An interesting question is: "why would I need an empty interface?" In most cases, empty interfaces are an indication of design errors. Here is one example:Ice 3.4.2 Documentation 114 Copyright © 2011, ZeroC, Inc. 1. 2. Slice interface ThingBase {}; interface Thing1 extends ThingBase { // Operations here... }; interface Thing2 extends ThingBase { // Operations here... }; Looking at this definition, we can make two observations: Thing1 and have a common base and are therefore related.Thing2 Whatever is common to and can be found in interface .Thing1 Thing2 ThingBase Of course, looking at , we find that and do not share any operations at all because is empty. GivenThingBase Thing1 Thing2 ThingBase that we are using an object-oriented paradigm, this is definitely strange: in the object-oriented model, the way to communicate with anonly object is to send a message to the object. But, to send a message, we need an operation. Given that has no operations, weThingBase cannot send a message to it, and it follows that and are related because they have no common operations. But ofThing1 Thing2 not course, seeing that and have a common base, we conclude that they related, otherwise the common base would notThing1 Thing2 are exist. At this point, most programmers begin to scratch their head and wonder what is going on here. One common use of the above design is a desire to treat and polymorphically. For example, we might continue theThing1 Thing2 previous definition as follows: Slice interface ThingUser { void putThing(ThingBase* thing); }; Now the purpose of having the common base becomes clear: we want to be able to pass both and proxies to .Thing1 Thing2 putThing Does this justify the empty base interface? To answer this question, we need to think about what happens in the implementation of . Obviously, cannot possibly invoke an operation on a because there are no operations. This means that putThing putThing ThingBase can do one of two things:putThing putThing can simply remember the value of .thing putThing can try to down-cast to either or and then invoke an operation. The pseudo-code for the implementationThing1 Thing2 of would look something like this:putThing void putThing(ThingBase thing) { if (is_a(Thing1, thing)) { // Do something with Thing1... } else if (is_a(Thing2, thing)) { // Do something with Thing2... } else { // Might be a ThingBase? // ... } } The implementation tries to down-cast its argument to each possible type in turn until it has found the actual run-time type of the argument. Of course, any object-oriented text book worth its price will tell you that this is an abuse of inheritance and leads to maintenance problems. If you find yourself writing operations such as that rely on artificial base interfaces, ask yourself whether you really need to doputThing things this way. For example, a more appropriate design might be:Ice 3.4.2 Documentation 115 Copyright © 2011, ZeroC, Inc. Slice interface Thing1 { // Operations here... }; interface Thing2 { // Operations here... }; interface ThingUser { void putThing1(Thing1* thing); void putThing2(Thing2* thing); }; With this design, and are not related, and offers a separate operation for each type of proxy. TheThing1 Thing2 ThingUser implementation of these operations does not need to use any down-casts, and all is well in our object-oriented world. Another common use of empty base interfaces is the following: Slice interface PersistentObject {}; interface Thing1 extends PersistentObject { // Operations here... }; interface Thing2 extends PersistentObject { // Operations here... }; Clearly, the intent of this design is to place persistence functionality into the base and require objectsPersistentObject implementation that want to have persistent state to inherit from . On the face of things, this is reasonable: after all, using inheritance inPersistentObject this way is a well-established design pattern, so what can possibly be wrong with it? As it turns out, there are a number of things that are wrong with this design: The above inheritance hierarchy is used to add to and . However, in a strict OO model, behavior can bebehavior Thing1 Thing2 invoked only by sending messages. But, because has no operations, no messages can be sent.PersistentObject This raises the question of how the implementation of actually goes about doing its job; presumably, it knowsPersistentObject something about the implementation (that is, the internal state) of and , so it can write that state into a database.Thing1 Thing2 But, if so, , , and can no longer be implemented in different address spaces because, in thatPersistentObject Thing1 Thing2 case, can no longer get at the state of and .PersistentObject Thing1 Thing2 Alternatively, and use some functionality provided by in order to make their internal stateThing1 Thing2 PersistentObject persistent. But does not have any operations, so how would and actually go about achievingPersistentObject Thing1 Thing2 this? Again, the only way that can work is if , , and are implemented in a single address spacePersistentObject Thing1 Thing2 and share implementation state behind the scenes, meaning that they cannot be implemented in different address spaces. The above inheritance hierarchy splits the world into two halves, one containing persistent objects and one containing non-persistent ones. This has far-reaching ramifications: Suppose you have an existing application with already implemented, non-persistent objects. Requirements change over time and you find that you now would like to make some of your objects persistent. With the above design, you cannot do this unless you change the type of your objects because they now must inherit from . Of course, this isPersistentObject extremely bad news: not only do you have to change the implementation of your objects in the server, you also need to locate and update all the clients that are currently using your objects because they suddenly have a completely new type. What is worse, there is no way to keep things backward compatible: either all clients change with the server, or none of them do. It is impossible for some clients to remain "unupgraded". The design does not scale to multiple features. Imagine that we have a number of additional behaviors that objects can inherit, such as serialization, fault-tolerance, persistence, and the ability to be searched by a search engine. We quickly end up in a mess of multiple inheritance. What is worse, each possible combination of features creates a completely separate type hierarchy. This means that you can no longer write operations that generically operate on a number of object types. For example, you cannot pass a persistent object to something that expects a non-persistent object, even if the receiver of . This quickly leads to fragmented and hard-to-maintainthe object does not care about the persistence aspects of the object type systems. Before long, you will either find yourself rewriting your application or end up with something that is both difficult to use and difficult to maintain.Ice 3.4.2 Documentation 116 Copyright © 2011, ZeroC, Inc. The foregoing discussion will hopefully serve as a warning: Slice is an definition language that has nothing to do with interface (but empty interfaces almost always indicate that implementation state is shared via mechanisms other than definedimplementation interfaces). If you find yourself writing an empty interface definition, at least step back and think about the problem at hand; there may be a more appropriate design that expresses your intent more cleanly. If you do decide to go ahead with an empty interface regardless, be aware that, almost certainly, you will lose the ability to later change the distribution of the object model over physical server processes because you cannot place an address space boundary between interfaces that share hidden state. Interface Versus Implementation Inheritance Keep in mind that Slice interface inheritance applies only to . In particular, if two interfaces are in an inheritance relationship, this ininterfaces no way implies that the implementations of those interfaces must also inherit from each other. You can choose to use implementation inheritance when you implement your interfaces, but you can also make the implementations independent of each other. (To C++ programmers, this often comes as a surprise because C++ uses implementation inheritance by default, and interface inheritance requires extra effort to implement.) In summary, Slice inheritance simply establishes type compatibility. It says nothing about how interfaces are implemented and, therefore, keeps implementation choices open to whatever is most appropriate for your application. See Also Interfaces, Operations, and Exceptions Operations User Exceptions Run-Time Exceptions Proxies IceGridIce 3.4.2 Documentation 117 Copyright © 2011, ZeroC, Inc. Classes In addition to , Slice permits the definition of classes. Classes are like interfaces in that they can have operations and are likeinterfaces structures in that they can have data members. This leads to hybrid objects that can be treated as interfaces and passed by reference, or can be treated as values and passed by value. Classes provide much architectural flexibility. For example, classes allow behavior to be implemented on the client side, whereas interfaces allow behavior to be implemented only on the server side. Classes support inheritance and are therefore polymorphic: at run time, you can pass a class instance to an operation as long as the actual class type is derived from the formal parameter type in the operation's signature. This also permits classes to be used as type-safe unions, similarly to Pascal's discriminated variant records. Topics Simple Classes Class Inheritance Class Inheritance Semantics Classes as Unions Self-Referential Classes Classes Versus Structures Classes with Operations Architectural Implications of Classes Classes Implementing Interfaces Class Inheritance Limitations Pass-by-Value Versus Pass-by-Reference Passing Interfaces by ValueIce 3.4.2 Documentation 118 Copyright © 2011, ZeroC, Inc. Simple Classes A Slice class definition is similar to a structure definition, but uses the keyword. For example:class Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; Apart from the keyword , this definition is identical to the example. You can use a Slice class wherever you can use a Sliceclass structure structure (but, as we will see shortly, for performance reasons, you should not use a class where a structure is sufficient). Unlike structures, classes can be empty: Slice class EmptyClass {}; // OK struct EmptyStruct {}; // Error Much the same design considerations as for apply to empty classes: you should at least stop and rethink your approachempty interfaces before committing yourself to an empty class. You can specify a default value for a class data member that has one of the following types: An type ( , , , )integral byte short int long A type ( or )floating point float double string bool enum For example: Slice class Location { string name; Point pt; bool display = true; string source = "GPS"; }; The legal syntax for literal values is the same as for , and you may also use a constant as a default value. The languageSlice constants mapping guarantees that data members are initialized to their declared default values using a language-specific mechanism. See Also Structures Constants and LiteralsIce 3.4.2 Documentation 119 Copyright © 2011, ZeroC, Inc. Class Inheritance Unlike , classes support inheritance. For example:structures Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; class DateTime extends TimeOfDay { short day; // 1 - 31 short month; // 1 - 12 short year; // 1753 onwards }; This example illustrates one major reason for using a class: a class can be extended by inheritance, whereas a structure is not extensible. The previous example defines to extend the class with a date.DateTime TimeOfDay If you are puzzled by the comment about the year 1753, search the Web for "1752 date change". The intricacies of calendars for various countries prior to that year can keep you occupied for months... Classes only support single inheritance. The following is illegal: Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; class Date { short day; short month; short year; }; class DateTime extends TimeOfDay, Date { // Error! // ... }; A derived class also cannot redefine a data member of its base class: Slice class Base { int integer; }; class Derived extends Base { int integer; // Error, integer redefined }; See Also StructuresIce 3.4.2 Documentation 120 Copyright © 2011, ZeroC, Inc.Ice 3.4.2 Documentation 121 Copyright © 2011, ZeroC, Inc. Class Inheritance Semantics Classes use the same pass-by-value semantics as . If you pass a class instance to an operation, the class and all its members arestructures passed. The usual type compatibility rules apply: you can pass a derived instance where a base instance is expected. If the receiver has static type knowledge of the actual derived run-time type, it receives the derived instance; otherwise, if the receiver does not have static type knowledge of the derived type, the instance is sliced to the base type. For an example, suppose we have the following definitions: Slice // In file Clock.ice: class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; interface Clock { TimeOfDay getTime(); void setTime(TimeOfDay time); }; // In file DateTime.ice: #include class DateTime extends TimeOfDay { short day; // 1 - 31 short month; // 1 - 12 short year; // 1753 onwards }; Because is a sub-class of , the server can return a instance from , and the client can pass a DateTime TimeOfDay DateTime getTime instance to . In this case, if both client and server are linked to include the code generated for both and DateTime setTime Clock.ice , they each receive the actual derived instance, that is, the actual run-time type of the instance is preserved.DateTime.ice DateTime Contrast this with the case where the server is linked to include the code generated for both and , but the clientClock.ice DateTime.ice is linked only with the code generated for . In other words, the server understands the type and can return a Clock.ice DateTime instance from , but the client only understands . In this case, the derived instance returned byDateTime getTime TimeOfDay DateTime the server is sliced to its base type in the client. (The information in the derived part of the instance is simply lost to the client.)TimeOfDay Class hierarchies are useful if you need polymorphic (instead of polymorphic ). For example:values interfaces Slice class Shape { // Definitions for shapes, such as size, center, etc. }; class Circle extends Shape { // Definitions for circles, such as radius... }; class Rectangle extends Shape { // Definitions for rectangles, such as width and length... }; sequence ShapeSeq; interface ShapeProcessor { void processShapes(ShapeSeq ss); };Ice 3.4.2 Documentation 122 Copyright © 2011, ZeroC, Inc. Note the definition of and its use as a parameter to the operation: the class hierarchy allows us to pass aShapeSeq processShapes polymorphic sequence of shapes (instead of having to define a separate operation for each type of shape). The receiver of a can iterate over the elements of the sequence and down-cast each element to its actual run-time type. (TheShapeSeq receiver can also ask each element for its to determine its type.)type ID See Also Structures Type IDsIce 3.4.2 Documentation 123 Copyright © 2011, ZeroC, Inc. Classes as Unions Slice does not offer a dedicated union construct because it is redundant. By deriving classes from a common base class, you can create the same effect as with a union: Slice interface ShapeShifter { Shape translate(Shape s, long xDistance, long yDistance); }; The parameter of the operation can be viewed as a union of two members: a and a . The receiver of a s translate Circle Rectangle instance can use the of the instance to decide whether it received a or a . Alternatively, if you wantShape type ID Circle Rectangle something more along the lines of a conventional discriminated union, you can use the following approach: Slice class UnionDiscriminator { int d; }; class Member1 extends UnionDiscriminator { // d == 1 string s; float f; }; class Member2 extends UnionDiscriminator { // d == 2 byte b; int i; }; With this approach, the class provides a discriminator value. The "members" of the union are the classes that areUnionDiscriminator derived from . For each derived class, the discriminator takes on a distinct value. The receiver of such a union usesUnionDiscriminator the discriminator value in a statement to select the active union member.switch See Also Type IDsIce 3.4.2 Documentation 124 Copyright © 2011, ZeroC, Inc. Self-Referential Classes Classes can be self-referential. For example: Slice class Link { SomeType value; Link next; }; This looks very similar to the , but the semantics are very different. Note that and are dataself-referential interface example value next members, not operations, and that the type of is ( ). As you would expect, this forms the same linked list arrangementnext Link not Link* as the interface in : each instance of a class contains a member that points at the next link in theLink Self-Referential Interfaces Link next chain; the final link's member contains a null value. So, what looks like a class including itself really expresses pointer semantics: the next data member contains a pointer to the next link in the chain.next You may be wondering at this point what the difference is then between the interface in and the classLink Self-Referential Interfaces Link shown above. The difference is that classes have semantics, whereas proxies have semantics. To illustrate this, considervalue reference the from once more:Link interface Self-Referential Interfaces Slice interface Link { idempotent SomeType getValue(); idempotent Link* next(); }; Here, and are both operations and the return value of is , that is, next returns a . A proxy has getValue next next Link* proxy reference semantics, that is, it denotes an object somewhere. If you invoke the operation on a proxy, a message is sent to thegetValue Link (possibly remote) servant for that proxy. In other words, for proxies, the object stays put in its server process and we access the state of the object via remote procedure calls. Compare this with the definition of our :Link class Slice class Link { SomeType value; Link next; }; Here, and are data members and the type of next is , which has semantics. In particular, while looks and feelsvalue next Link value next like a pointer, . This means that if we have a chain of instances, all of theit cannot denote an instance in a different address space Link instances are in our local address space and, when we read or write a value data member, we are performing local address space operations. This means that an operation that returns a instance, such as , does not just return the head of the chain, Link getHead but the , as shown:entire chainIce 3.4.2 Documentation 125 Copyright © 2011, ZeroC, Inc. Class version of before and after calling .Link getHead On the other hand, for the interface version of , we do not know where all the links are physically implemented. For example, a chain ofLink four links could have each object instance in its own physical server process; those server processes could be each in a different continent. If you have a proxy to the head of this four-link chain and traverse the chain by invoking the operation on each link, you will be sendingnext four remote procedure calls, one to each object. Self-referential classes are particularly useful to model graphs. For example, we can create a simple expression tree along the following lines: Slice enum UnaryOp { UnaryPlus, UnaryMinus, Not }; enum BinaryOp { Plus, Minus, Multiply, Divide, And, Or }; class Node {}; class UnaryOperator extends Node { UnaryOp operator; Node operand; }; class BinaryOperator extends Node { BinaryOp op; Node operand1; Node operand2; }; class Operand extends Node { long val; }; The expression tree consists of leaf nodes of type , and interior nodes of type and , with oneOperand UnaryOperator BinaryOperator or two descendants, respectively. All three of these classes are derived from a common base class . Note that is an empty class.Node Node This is one of the few cases where an empty base class is justified. (See the discussion on ; once we add to thisempty interfaces operations class hierarchy, the base class is no longer empty.) If we write an operation that, for example, accepts a parameter, passing that parameter results in transmission of the entire tree to theNode server: Slice interface Evaluator { long eval(Node expression); // Send entire tree for evaluation };Ice 3.4.2 Documentation 126 Copyright © 2011, ZeroC, Inc. Self-referential classes are not limited to acyclic graphs; the Ice run time permits loops: it ensures that no resources are leaked and that infinite loops are avoided during marshaling. See Also Classes with Operations Self-Referential InterfacesIce 3.4.2 Documentation 127 Copyright © 2011, ZeroC, Inc. Classes Versus Structures One obvious question to ask is: why does Ice provide as well as classes, when classes obviously can be used to modelstructures structures? The answer has to do with the cost of implementation: classes provide a number of features that are absent for structures: Classes support inheritance. Classes can be self-referential. Classes can have .operations Classes can .implement interfaces Obviously, an implementation cost is associated with the additional features of classes, both in terms of the size of the generated code and the amount of memory and CPU cycles consumed at run time. On the other hand, structures are simple collections of values ("plain old structs") and are implemented using very efficient mechanisms. This means that, if you use structures, you can expect better performance and smaller memory footprint than if you would use classes (especially for languages with direct support for "plain old structures", such as C++ and C#). Use a class only if you need at least one of its more powerful features. See Also Structures Classes with Operations Classes Implementing InterfacesIce 3.4.2 Documentation 128 Copyright © 2011, ZeroC, Inc. Classes with Operations Classes, in addition to data members, can have operations. The syntax for operation definitions in classes is identical to the syntax for operations in interfaces. For example, we can modify the expression tree from as follows:Self-Referential Classes Slice enum UnaryOp { UnaryPlus, UnaryMinus, Not }; enum BinaryOp { Plus, Minus, Multiply, Divide, And, Or }; class Node { idempotent long eval(); }; class UnaryOperator extends Node { UnaryOp operator; Node operand; }; class BinaryOperator extends Node { BinaryOp op; Node operand1; Node operand2; }; class Operand { long val; }; The only change compared to the version in is that the class now has an operation. The semantics ofSelf-Referential Classes Node eval this are as for a virtual member function in C++: each derived class inherits the operation from its base class and can choose to override the operation's definition. For our expression tree, the class provides an implementation that simply returns the value of its Operand val member, and the and classes provide implementations that compute the value of their respectiveUnaryOperator BinaryOperator subtrees. If we call on the root node of an expression tree, it returns the value of that tree, regardless of whether we have a complexeval expression or a tree that consists of only a single node.Operand Operations on classes are normally executed in the caller's address space, that is, operations on classes are operations that do notlocal result in a remote procedure call. It is also possible to invoke an operation on a .remote class instance Of course, this immediately raises an interesting question: what happens if a client receives a class instance with operations from a server, but client and server are implemented in different languages? Classes with operations require the receiver to supply a factory for instances of the class. The Ice run time only marshals the data members of the class. If a class has operations, the receiver of the class must provide a class factory that can instantiate the class in the receiver's address space, and the receiver is responsible for providing an implementation of the class's operations. Therefore, if you use classes with operations, it is understood that client and server each have access to an implementation of the class's operations. No code is shipped over the wire (which, in an environment of heterogeneous nodes using different operating systems and languages is infeasible). See Also Self-Referential Classes Pass-by-Value Versus Pass-by-ReferenceIce 3.4.2 Documentation 129 Copyright © 2011, ZeroC, Inc. Architectural Implications of Classes Classes have a number of architectural implications that are worth exploring in some detail. On this page: Classes without Operations Classes with Operations Classes for Persistence Classes without Operations Classes that do not use inheritance and only have data members (whether self-referential or not) pose no architectural problems: they simply are values that are marshaled like any other value, such as a sequence, structure, or dictionary. Classes using derivation also pose no problems: if the receiver of a derived instance has knowledge of the derived type, it simply receives the derived type; otherwise, the instance is sliced to the most-derived type that is understood by the receiver. This makes class inheritance useful as a system is extended over time: you can create derived class without having to upgrade all parts of the system at once. Classes with Operations Classes with operations require additional thought. Here is an example: suppose that you are creating an Ice application. Also assume that the Slice definitions use quite a few classes with operations. You sell your clients and servers (both written in Java) and end up with thousands of deployed systems. As time passes and requirements change, you notice a demand for clients written in C++. For commercial reasons, you would like to leave the development of C++ clients to customers or a third party but, at this point, you discover a glitch: your application has lots of classes with operations along the following lines: Slice class ComplexThingForExpertsOnly { // Lots of arcane data members here... MysteriousThing mysteriousOperation(/* parameters */); ArcaneThing arcaneOperation(/* parameters */); ComplexThing complexOperation(/* parameters */); // etc... }; It does not matter what exactly these operations do. (Presumably, you decided to off-load some of the processing for your application onto the client side for performance reasons.) Now that you would like other developers to write C++ clients, it turns out that your application will work only if these developers provide implementations of all the client-side operations and, moreover, if the semantics of these operations exactly match the semantics of your Java implementations. Depending on what these operations do, providing exact semantic equivalents in a different language may not be trivial, so you decide to supply the C++ implementations yourself. But now, you discover another problem: the C++ clients need to be supported for a variety of operating systems that use a variety of different C++ compilers. Suddenly, your task has become quite daunting: you really need to supply implementations for all the combinations of operating systems and compiler versions that are used by clients. Given the different state of compliance with the ISO C++ standard of the various compilers, and the idiosyncrasies of different operating systems, you may find yourself facing a development task that is much larger than anticipated. And, of course, the same scenario will arise again should you need client implementations in yet another language. The moral of this story is not that classes with operations should be avoided; they can provide significant performance gains and are not necessarily bad. But, keep in mind that, once you use classes with operations, you are, in effect, using client-side native code and, therefore, you can no longer enjoy the implementation transparencies that are provided by interfaces. This means that classes with operations should be used only if you can tightly control the deployment environment of clients. If not, you are better off using interfaces and classes without operations. That way, all the processing stays on the server and the contract between client and server is provided solely by the Slice definitions, not by the semantics of the additional client-side code that is required for classes with operations. Classes for Persistence Ice also provides a built-in that allows you to store the state of a class in a database with very little implementationpersistence mechanism effort. To get access to these persistence features, you must define a Slice class whose members store the state of the class. See AlsoIce 3.4.2 Documentation 130 Copyright © 2011, ZeroC, Inc. FreezeIce 3.4.2 Documentation 131 Copyright © 2011, ZeroC, Inc. Classes Implementing Interfaces A Slice class can also be used as a servant in a server, that is, an instance of a class can be used to provide the behavior for an interface, for example: Slice interface Time { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time); }; class Clock implements Time { TimeOfDay time; }; The keyword indicates that the class provides an of the interface. The class can provide dataimplements Clock implementation Time members and operations of its own; in the preceding example, the class stores the current time that is accessed via the Clock Time interface. A class can implement several interfaces, for example: Slice interface Time { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time); }; interface Radio { idempotent void setFrequency(long hertz); idempotent void setVolume(long dB); }; class RadioClock implements Time, Radio { TimeOfDay time; long hertz; }; The class implements both and interfaces.RadioClock Time Radio A class, in addition to implementing an interface, can also extend another class:Ice 3.4.2 Documentation 132 Copyright © 2011, ZeroC, Inc. Slice interface Time { idempotent TimeOfDay getTime(); idempotent void setTime(TimeOfDay time); }; class Clock implements Time { TimeOfDay time; }; interface AlarmClock extends Time { idempotent TimeOfDay getAlarmTime(); idempotent void setAlarmTime(TimeOfDay alarmTime); }; interface Radio { idempotent void setFrequency(long hertz); idempotent void setVolume(long dB); }; class RadioAlarmClock extends Clock implements AlarmClock, Radio { TimeOfDay alarmTime; long hertz; }; These definitions result in the following inheritance graph: A Class using implementation and interface inheritance. For this definition, and are abstract interfaces, and and are concrete classes. As for Java,Radio AlarmClock Clock RadioAlarmClock a class can implement multiple interfaces, but can extend at most one class. See Also Architectural Implications of Classes Class Inheritance LimitationsIce 3.4.2 Documentation 133 Copyright © 2011, ZeroC, Inc. Class Inheritance Limitations As for , a class cannot redefine an operation or data member that it inherits from a base interface or class. For example:interface inheritance Slice interface BaseInterface { void op(); }; class BaseClass { int member; }; class DerivedClass extends BaseClass implements BaseInterface { void someOperation(); // OK int op(); // Error! int someMember; // OK long member; // Error! }; See Also Interface InheritanceIce 3.4.2 Documentation 134 Copyright © 2011, ZeroC, Inc. Pass-by-Value Versus Pass-by-Reference As we saw in , classes naturally support pass-by-value semantics: passing a class transmits the data members ofSelf-Referential Classes the class to the receiver. Any changes made to these data members by the receiver affect only the receiver's copy of the class; the data members of the sender's class are not affected by the changes made by the receiver. In addition to passing a class by value, you can pass a class by reference. For example: Slice class TimeOfDay { short hour; short minute; short second; string format(); }; interface Example { TimeOfDay* get(); // Note: returns a proxy! }; Note that the operation returns a to a class and not a instance itself. The semantics of this are asget proxy TimeOfDay TimeOfDay follows: When the client receives a proxy from the call, it holds a proxy that differs in no way from an ordinary proxy for anTimeOfDay get interface. The client can invoke operations via the proxy, but access the data members. This is because proxies do not have thecannot concept of data members, but represent interfaces: even though the class has data members, only its canTimeOfDay operations be accessed via a the proxy. The net effect is that, in the preceding example, the server holds an instance of the class. A proxy for that instance was passedTimeOfDay to the client. The only thing the client can do with this proxy is to invoke the operation. The implementation of that operation isformat provided by the server and, when the client invokes , it sends an RPC message to the server just as it does when it invokes anformat operation on an interface. The implementation of the operation is entirely up to the server. (Presumably, the server will use the dataformat members of the instance it holds to return a string containing the time to the client.)TimeOfDay The preceding example looks somewhat contrived for classes only. However, it makes perfect sense if classes implement interfaces: parts of your application can exchange class instances (and, therefore, state) by value, whereas other parts of the system can treat these instances as remote interfaces. For example: Slice interface Time { string format(); // ... }; class TimeOfDay implements Time { short hour; short minute; short second; }; interface I1 { TimeOfDay get(); // Pass by value void put(TimeOfDay time); // Pass by value }; interface I2 { Time* get(); // Pass by reference };Ice 3.4.2 Documentation 135 Copyright © 2011, ZeroC, Inc. In this example, clients dealing with interface are aware of the class and pass it by value whereas clients dealing withI1 TimeOfDay interface deal only with the interface. However, the actual implementation of the interface in the server uses I2 Time Time TimeOfDay instances. Be careful when designing systems that use such mixed pass-by-value and pass-by-reference semantics. Unless you are clear about what parts of the system deal with the interface (pass by reference) aspects and the class (pass by value) aspects, you can end up with something that is more confusing than helpful. A good example of putting this feature to use can be found in , which allows you to add classes to an existing interface to implementFreeze persistence. See Also Self-Referential Classes FreezeIce 3.4.2 Documentation 136 Copyright © 2011, ZeroC, Inc. Passing Interfaces by Value Consider the following definitions: Slice interface Time { idempotent TimeOfDay getTime(); // ... }; interface Record { void addTimeStamp(Time t); // Note: Time t, not Time* t // ... }; Note that accepts a parameter of type , not of type . The question is, what does it mean to pass an interface addTimeStamp Time Time* by ? Obviously, at run time, we cannot pass an an actual interface to this operation because interfaces are abstract and cannot bevalue instantiated. Neither can we pass a proxy to a object to because a proxy cannot be passed where an interface isTime addTimeStamp expected. However, what we pass to is something that is not abstract and derives from the interface. For example, at runcan addTimeStamp Time time, we could pass an instance of the class we saw . Because the class derives from the interface,TimeOfDay earlier TimeOfDay Time the class type is compatible with the formal parameter type and, at run time, what is sent over the wire to the server is the Time TimeOfDay class instance. See Also Pass-by-Value Versus Pass-by-ReferenceIce 3.4.2 Documentation 137 Copyright © 2011, ZeroC, Inc. Forward Declarations Both and can be forward declared. Forward declarations permit the creation of mutually dependent objects, for example:interfaces classes Slice module Family { interface Child; // Forward declaration sequence Children; // OK interface Parent { Children getChildren(); // OK }; interface Child { // Definition Parent* getMother(); Parent* getFather(); }; }; Without the forward declaration of , the definition obviously could not compile because and are mutually dependentChild Child Parent interfaces. You can use forward-declared interfaces and classes to define types (such as the sequence in the previous example).Children Forward-declared interfaces and classes are also legal as the type of a structure, exception, or class member, as the value type of a dictionary, and as the parameter and return type of an operation. However, you cannot inherit from a forward-declared interface or class until after its definition has been seen by the compiler: Slice interface Base; // Forward declaration interface Derived1 extends Base {}; // Error! interface Base {}; // Definition interface Derived2 extends Base {}; // OK, definition was seen Not inheriting from a forward-declared base interface or class until its definition is seen is necessary because, otherwise, the compiler could not enforce that derived interfaces must not redefine operations that appear in base interfaces. A multi-pass compiler could be used, but the added complexity is not worth it. See Also Interfaces, Operations, and Exceptions ClassesIce 3.4.2 Documentation 138 Copyright © 2011, ZeroC, Inc. Type IDs Each user-defined Slice type has an internal type identifier, known as its . The type ID is simply the fully-qualified name of each type.type ID For example, the type ID of the interface in the is . All type IDs for user-definedChild preceding example ::Family::Children::Child types start with a leading , so the type ID of the module is (not ). In general, a type ID is formed by starting:: Family ::Family Family with the global scope ( ) and forming the fully-qualified name of a type by appending each module name in which the type is nested, and:: ending with the name of the type itself; the components of the type ID are separated by .:: The type ID of a proxy is formed by appending a to the type ID of an interface or class. For example, the type ID of a proxy is * Child .::Family::Children::Child* The type ID of the Slice type is and the type ID of an proxy is .Object ::Ice::Object Object ::Ice::Object* The type IDs for the remaining built-in types, such as , , and so on, are the same as the corresponding keyword. For example, theint bool type ID of is , and the type ID of is .int int string string Type IDs are used internally by the Ice run time as a unique identifier for each type. For example, when an exception is raised, the marshaled form of the exception that is returned to the client is preceded by its type ID on the wire. The client-side run time first reads the type ID and, based on that, unmarshals the remainder of the data as appropriate for the type of the exception. Type IDs are also used by the . operationice_isA See Also ice_isAIce 3.4.2 Documentation 139 Copyright © 2011, ZeroC, Inc. Operations on Object The interface has a number of operations. We cannot define type in Slice because is a keyword; regardless, hereObject Object Object is what (part of) the definition of would look like if it were legal:Object Slice sequence StrSeq; interface Object { // "Pseudo" Slice! idempotent void ice_ping(); idempotent bool ice_isA(string typeID); idempotent string ice_id(); idempotent StrSeq ice_ids(); // ... }; Note that, apart from the illegal use of the keyword as the interface name, the operation names all contain the prefix. ThisObject ice_ prefix is reserved for use by Ice and cannot clash with a user-defined operation. This means that all Slice interfaces can inherit from Object without name clashes. We discuss these built-in operations below. On this page: ice_ping ice_isA ice_id ice_ids ice_ping All interfaces support the operation. That operation is useful for debugging because it provides a basic reachability test for anice_ping object: if the object exists and a message can successfully be dispatched to the object, simply returns without error. If the objectice_ping cannot be reached or does not exist, throws a run-time exception that provides the reason for the failure.ice_ping ice_isA The operation accepts a type identifier (such as the identifier returned by ) and tests whether the target object supports theice_isA ice_id specified type, returning if it does. You can use this operation to check whether a target object supports a particular type. For example,true referring to the diagram once more, assume that you are holding a proxy to a target object of type Implicit Inheritance from Object . The table below illustrates the result of calling on that proxy with various arguments. (We assume that all types inAlarmClock ice_isA the diagram are defined in a module ):Implicit inheritance from Object Times Argument Result ::Ice::Object true ::Times::Clock true ::Times::AlarmClock true ::Times::Radio false ::Times::RadioClock false Calling on a proxy denoting an object of type AlarmClock.ice_isA As expected, returns true for and and also returns true for ice_isA ::Times::Clock ::Times::AlarmClock ::Ice::Object (because all interfaces support that type). Obviously, an supports neither the nor the interfaces, so AlarmClock Radio RadioClock returns false for these types.ice_isAIce 3.4.2 Documentation 140 Copyright © 2011, ZeroC, Inc. ice_id The operation returns the of the most-derived type of an interface.ice_id type ID ice_ids The operation returns a sequence of that contains all of the type IDs supported by an interface. For example, for theice_ids type IDs RadioClock interface in , returns a sequence containing the type IDs , Implicit inheritance from Object ice_ids ::Ice::Object , , , and .::Times::Clock ::Times::AlarmClock ::Times::Radio ::Times::RadioClock See Also Type IDs Interface Inheritance Implicit inheritance from ObjectIce 3.4.2 Documentation 141 Copyright © 2011, ZeroC, Inc. Local Types In order to access certain features of the Ice run time, you must use APIs that are provided by libraries. However, instead of defining an API that is specific to each implementation language, Ice defines its APIs in Slice using the keyword. The advantage of defining APIs inlocal Slice is that a single definition suffices to define the API for all possible implementation languages. The actual language-specific API is then generated by the Slice compiler for each implementation language. Types that are provided by Ice libraries are defined using the Slice keyword.local For example: Slice module Ice { local interface ObjectAdapter { // ... }; }; Any Slice definition (not just interfaces) can have a modifier. If the modifier is present, the Slice compiler does not generatelocal local marshaling code for the corresponding type. This means that a local type can be accessed remotely because it cannot be transmittednever between client and server. (The Slice compiler prevents use of types in non- contexts.)local local In addition, local interfaces and local classes do inherit from . Instead, local interfaces and classes have their own,not Ice::Object completely separate inheritance hierarchy. At the root of this hierarchy is the type , as shown:Ice::LocalObject Inheritance from .LocalObject Because local interfaces form a completely separate inheritance hierarchy, you cannot pass a local interface where a non-local interface is expected, and vice-versa. You rarely need to define local types for your own applications — the keyword exists mainly to allow definition of APIs for the Ice runlocal time. (Because local objects cannot be invoked remotely, there is little point for an application to define local objects; it might as well define ordinary programming-language objects instead.) However, there is one exception to this rule: must be implemented asservant locators local objects. See Also Servant LocatorsIce 3.4.2 Documentation 142 Copyright © 2011, ZeroC, Inc. Names and Scoping Slice has a number of rules regarding identifiers. You will typically not have to concern yourself with these. However, occasionally, it is good to know how Slice uses naming scopes and resolves identifiers. On this page: Naming Scope Case Sensitivity Qualified Names Names in Nested Scopes Introduced Identifiers Name Lookup Rules Naming Scope The following Slice constructs establish a naming scope: the global (file) scope modules interfaces classes structures exceptions parameter lists Within a naming scope, identifiers must be unique, that is, you cannot use the same identifier for different purposes. For example: Slice interface Bad { void op(int p, string p); // Error! }; Because a parameter list forms a naming scope, it is illegal to use the same identifier for different parameters. Similarly, data members,p operation names, interface and class names, etc. must be unique within their enclosing scope. Case Sensitivity Identifiers that differ only in case are considered identical, so you must use identifiers that differ not only in capitalization within a naming scope. For example: Slice struct Bad { int m; string M; // Error! }; The Slice compiler also enforces consistent capitalization for identifiers. Once you have defined an identifier, you must use the same capitalization for that identifier thereafter. For example, the following is in error: Slice sequence StringSeq; interface Bad { stringSeq op(); // Error! };Ice 3.4.2 Documentation 143 Copyright © 2011, ZeroC, Inc. Note that identifiers must not differ from a Slice keyword in case only. For example, the following is in error: Slice interface Module { // Error, "module" is a keyword // ... }; Qualified Names The scope-qualification operator allows you to refer to a type in a non-local scope. For example::: Slice module Types { sequence LongSeq; }; module MyApp { sequence NumberTree; }; Here, the qualified name refers to defined in module . The global scope is denoted by a leading , soTypes::LongSeq LongSeq Types :: we could also refer to as .LongSeq ::Types::LongSeq The scope-qualification operator also allows you to create mutually dependent interfaces that are defined in different modules. The obvious attempt to do this fails: Slice module Parents { interface Children::Child; // Syntax error! interface Mother { Children::Child* getChild(); }; interface Father { Children::Child* getChild(); }; }; module Children { interface Child { Parents::Mother* getMother(); Parents::Father* getFather(); }; }; This fails because it is syntactically illegal to forward-declare an interface in a different module. To make it work, we must use a reopened module:Ice 3.4.2 Documentation 144 Copyright © 2011, ZeroC, Inc. Slice module Children { interface Child; // Forward declaration }; module Parents { interface Mother { Children::Child* getChild(); // OK }; interface Father { Children::Child* getChild(); // OK }; }; module Children { // Reopen module interface Child { // Define Child Parents::Mother* getMother(); Parents::Father* getFather(); }; }; While this technique works, it is probably of dubious value: mutually dependent interfaces are, by definition, tightly coupled. On the other hand, modules are meant to be used to place related definitions into the same module, and unrelated definitions into different modules. Of course, this begs the question: if the interfaces are so closely related that they depend on each other, why are they defined in different modules? In the interest of clarity, you probably should avoid this construct, even though it is legal. Names in Nested Scopes Names defined in an enclosing scope can be redefined in an inner scope. For example, the following is legal: Slice module Outer { sequence Seq; module Inner { sequence Seq; }; }; Within module , the name refers to a sequence of values and hides the definition of . You can still refer toInner Seq short Outer::Seq the other definition by using explicit scope qualification, for example: Slice module Outer { sequence Seq; module Inner { sequence Seq; struct Confusing { Seq a; // Sequence of short ::Outer::Seq b; // Sequence of string }; }; }; Needless to say, you should try to avoid such redefinitions — they make it harder for the reader to follow the meaning of a specification. Same-named constructs cannot be nested inside each other. For example, a module named cannot (recursively) contain any constructMIce 3.4.2 Documentation 145 Copyright © 2011, ZeroC, Inc. also named . The same is true for interfaces, classes, structures, exceptions, and operations. For example, the following examples are all inM error: Slice module M { interface M { /* ... */ }; // Error! interface I { void I(); // Error! void op(string op); // Error! }; struct S { long s; // Error, even if case differs! }; }; module Outer { module Inner { interface Outer { // Error! // ... }; }; }; The reason for this restriction is that nested types that have the same name are difficult to map into some languages. For example, C++ and Java reserve the name of a class as the name of the constructor, so an interface could not contain an operation named without artificialI I rules to avoid the name clash. Similarly, some languages (such as C# prior to version 2.0) do not permit a qualified name to be anchored at the global scope. If a nested module or type is permitted to have the same name as the name of an enclosing module, it can become impossible to generate legal code in some cases. In the interest of simplicity, Slice prohibits the name of a nested module or type to be the same as the name of one of its enclosing modules. Introduced Identifiers Within a naming scope, an identifier is introduced at the point of first use; thereafter, within that naming scope, the identifier cannot change meaning. For example: Slice module M { sequence Seq; interface Bad { Seq op1(); // Seq and op1 introduced here int Seq(); // Error, Seq has changed meaning }; }; The declaration of uses as its return type, thereby introducing into the scope of interface . Thereafter, can only beop1 Seq Seq Bad Seq used as a type name that denotes a sequence of strings, so the compiler flags the declaration of the second operation as an error. Note that fully-qualified identifiers are not introduced into the current scope:Ice 3.4.2 Documentation 146 Copyright © 2011, ZeroC, Inc. Slice module M { sequence Seq; interface Bad { ::M::Seq op1(); // Only op1 introduced here int Seq(); // OK }; }; In general, a fully-qualified name (one that is anchored at the global scope and, therefore, begins with a scope resolution operator) does:: not introduce any name into the current scope. On the other hand, a qualified name that is not anchored at the global scope introduces only the first component of the name: Slice module M { sequence Seq; interface Bad { M::Seq op1(); // M and op1 introduced here, but not Seq int Seq(); // OK }; }; Name Lookup Rules When searching for the definition of a name that is not anchored at the global scope, the compiler first searches backward in the current scope of a definition of the name. If it can find the name in the current scope, it uses that definition. Otherwise, the compiler successively searches enclosing scopes for the name until it reaches the global scope. Here is an example to illustrate this: Slice module M1 { sequence Seq; module M2 { sequence Seq; // OK, hides ::M1::Seq interface Base { Seq op1(); // Returns sequence of string }; }; module M3 { interface Derived extends M2::Base { Seq op2(); // Returns sequence of double }; sequence Seq; // OK, hides ::M1::Seq interface I { Seq op(); // Returns sequence of bool }; }; interface I { Seq op(); // Returns sequence of double }; };Ice 3.4.2 Documentation 147 Copyright © 2011, ZeroC, Inc. Note that returns a sequence of , even though returns a sequence of . That is, theM2::Derived::op2 double M1::Base::op1 string meaning of a type in a base interface is irrelevant to determining its meaning in a derived interface — the compiler always searches for a definition only in the current scope and enclosing scopes, and never takes the meaning of a name from a base interface or class. See Also Lexical RulesIce 3.4.2 Documentation 148 Copyright © 2011, ZeroC, Inc. Metadata Slice has the concept of a directive. For example:metadata Slice ["java:type:java.util.LinkedList"] sequence IntSeq; A metadata directive can appear as a prefix to any Slice definition. Metadata directives appear in a pair of square brackets and contain one or more string literals separated by commas. For example, the following is a syntactically valid metadata directive containing two strings: Slice ["a", "b"] interface Example {}; Metadata directives are not part of the Slice language per se: the presence of a metadata directive has no effect on the client-server contract, that is, metadata directives do not change the Slice type system in any way. Instead, metadata directives are targeted at specific back-ends, such as the code generator for a particular language mapping. In the preceding example, the prefix indicates that thejava: directive is targeted at the Java code generator. Metadata directives permit you to provide supplementary information that does not change the Slice types being defined, but somehow influences how the compiler will generate code for these definitions. For example, a metadata directive instructs the Java code generator to map a sequence to a linked list instead of an array (which isjava:type:java.util.LinkedList the default). Metadata directives are also used to create skeletons that support .Asynchronous Method Dispatch (AMD) Apart from metadata directives that are attached to a specific definition, there are also global metadata directives. For example: Slice [["java:package:com.acme"]] Note that a global metadata directive is enclosed by double square brackets, whereas a local metadata directive (one that is attached to a specific definition) is enclosed by single square brackets. Global metadata directives are used to pass instructions that affect the entire compilation unit. For example, the preceding metadata directive instructs the Java code generator to generate the contents of the source file into the Java package . Global metadata directives must precede any definitions in a file (but can appear following any com.acme #include directives). We discuss specific metadata directives in the relevant chapters to which they apply. You can find a summary of all metadata directives in .Slice Metadata Directives See Also Slice Metadata DirectivesIce 3.4.2 Documentation 149 Copyright © 2011, ZeroC, Inc. Serializable Objects Ice for Java and Ice for .NET allow you to send native Java and CLR objects as operation parameters. The Ice run time automatically serializes and deserializes the objects as part of an invocation. This mechanism allows you to transmit Java and CLR objects that do not have a corresponding Slice definition. On this page: The Metadata Directiveserializable Architectural Implications The Metadata Directiveserializable To enable serialization, the parameter type must be a byte sequence with appropriate metadata. For example: Slice ["java:serializable:SomePackage.JavaClass"] sequence JavaObj; interface JavaExample { void sendJavaObj(JavaObj o); }; ["clr:serializable:SomeNamespace.CLRClass"] sequence CLRObj; interface CLRExample { void sendCLRObj(CLRObj o); }; The metadata indicates that the corresponding byte sequence holds a named java:serializable Java serializable type . Your program must provide an implementation of this class; the class must be derived from SomePackage.JavaClass .java.io.Serializable Similarly, the metadata indicates that the corresponding byte sequences holds a named clr:serializable CLR serializable type . Your program must provide an implementation of this class; the class must be marked with the SomeNamespace.CLRClass attribute.Serializable Architectural Implications The metadata directive permits you to transmit arbitrary Java and CLR objects across the network without the need toserializable define corresponding Slice classes or structures. This is mainly a convenience feature: you could achieve the same thing by using ordinary Slice byte sequences and explicitly serializing your Java or CLR objects into byte sequences at the sending end, and deserializing them at the receiving end. The metadata conveniently takes care of these chores for you and so is simpler to use.serializable Despite its convenience, you should use this feature with caution because it destroys language transparency. For example, a serialized Java object is useless to a C++ server. All the C++ server can do with such an object is to pass it on to some other process as a byte sequence. (Of course, if that receiving process is a Java process, it can deserialize the byte sequence.) Further, similar to Slice , a serialized object can be deserialized only if client and server agree on the definition of theclasses with methods serialized class. In Java, this is enforced by the field of each instance; in the CLR, client and server must referenceserialVersionUID identical assembly versions. This creates much tighter coupling of client and server than exchanging Slice-defined types. And, of course, if you build a system that relies on, for example, the exchange of serialized Java objects and you later find that you need to add C++ or C# components to the system, these components cannot do anything with the serialized Java objects other than pass them around as a blob of bytes. So, if you do use these features, be clear that this implies tighter coupling between client and server, and that it creates additional library versioning and distribution issues because all parts of the system must agree on the implementation of the serialized objects. See AlsoIce 3.4.2 Documentation 150 Copyright © 2011, ZeroC, Inc. Serializable Objects in Java Serializable Objects in C# Architectural Implications of ClassesIce 3.4.2 Documentation 151 Copyright © 2011, ZeroC, Inc. Deprecating Slice Definitions All Slice compilers support a metadata directive that allows you to deprecate a Slice definition. For example: Slice interface Example { ["deprecated:someOperation() has been deprecated, use alternativeOperation() instead."] void someOperation(); void alternativeOperation(); }; The metadata directive causes the compiler to emit code that generates a warning if you compile application code that["deprecated"] uses a deprecated feature. This is useful if you want to remove a feature from a Slice definition but do not want to cause a hard error. The message that follows the colon is optional; if you omit the message and use , the Slice compilers insert a default["deprecated"] message into the generated code. You can apply the metadata directive to Slice constructs other than operations (for example, a structure or sequence["deprecated"] definition). See Also Generating Slice DocumentationIce 3.4.2 Documentation 152 Copyright © 2011, ZeroC, Inc. Using the Slice Compilers Ice provides a separate Slice compiler for each language mapping, as shown below: Language Compiler C++ slice2cpp Java slice2java C# slice2cs Objective-C slice2objc Python slice2py Ruby slice2rb PHP slice2php The Slice compilers. The compilers share a similar command-line syntax: [options] file... Regardless of which compiler you use, a number of command-line options are common to the compilers for any language mapping. (See the appropriate language mapping chapter for options that are specific to a particular language mapping.) The common command-line options are: -h, --help Displays a help message. -v, --version Displays the compiler version. -DNAME Defines the preprocessor symbol .NAME -D =NAME DEF Defines the preprocessor symbol with the value .NAME DEF -UNAME Undefines the preprocessor symbol { .NAME -IDIR Add the directory to the search path for directives.DIR #include -E Print the preprocessor output on .stdout --output-dir DIR Place the generated files into directory .DIR -d, --debug Print debug information showing the operation of the Slice parser. --ice Permit use of the normally reserved prefix for identifiers. Use this option only when compiling the source code for the Ice runIce time. --underscore Permit use of underscores in Slice identifiers. The Slice compilers permit you to compile more than a single source file, so you can compile several Slice definitions at once, for example:Ice 3.4.2 Documentation 153 Copyright © 2011, ZeroC, Inc. slice2cpp -I. file1.ice file2.ice file3.ice See Also Slice CompilationIce 3.4.2 Documentation 154 Copyright © 2011, ZeroC, Inc. Slice Checksums As distributed applications evolve, developers and system administrators must be careful to ensure that deployed components are using the same client-server contract. Unfortunately, mistakes do happen, and it is not always readily apparent when they do. To minimize the chances of this situation, the Slice compilers support an option that generates checksums for Slice definitions, thereby enabling two peers to verify that they share an identical client-server contract. The checksum for a Slice definition includes details such as parameter and member names and the order in which operations are defined, but ignores information that is not relevant to the client-server contract, such as metadata, comments, and formatting. This option causes the Slice compiler to construct a dictionary that maps Slice type identifiers to checksums. A server typically supplies an operation that returns its checksum dictionary for the client to compare with its local version, at which point the client can take action if it discovers a mismatch. The dictionary type is defined in the file as follows:Ice/SliceChecksumDict.ice Slice module Ice { dictionary SliceChecksumDict; }; This type can be incorporated into an application's Slice definitions like this: Slice #include interface MyServer { idempotent Ice::SliceChecksumDict getSliceChecksums(); // ... }; The key of each element in the dictionary is a Slice , and the value is the checksum of that type.type ID For more information on generating and using Slice checksums, see the appropriate language mapping chapter. See Also Type IDsIce 3.4.2 Documentation 155 Copyright © 2011, ZeroC, Inc. Generating Slice Documentation On this page: Generating Slice Documentation Documentation Comments Hyperlinks Explicit Cross-References Markup for Operations General HTML Markup Using slice2html Generating Slice Documentation If you look at the online , you will find reference documentation for all the Slice definitions used by Ice and its services. InSlice API reference the binary distributions of Ice, you will also find HTML documentation that contains the same information. The HTML documentation is generated from special comments in the Slice definitions using , a tool that scans Slice definitions for special comments andslice2html generates HTML pages for those comments. As an example of documentation comments, here is the definition of :Ice::CurrentIce 3.4.2 Documentation 156 Copyright © 2011, ZeroC, Inc. Slice /** * * Information about the current method invocation for servers. * Each operation on the server has a [Current] as its implicit * final parameter. [Current] is mostly used for Ice services. * Most applications ignore this parameter. * **/ local struct Current { /** * The object adapter. **/ ObjectAdapter adapter; /** * Information about the connection over which the current * method invocation was received. If the invocation is direct * due to collocation optimization, this value is set to null. **/ Connection con; /** * The Ice object identity. **/ Identity id; /** * The facet. ***/ string facet; /** * The operation name. **/ string operation; /** * The mode of the operation. **/ OperationMode mode; /** * The request context, as received from the client. **/ Context ctx; /** * The request id unless oneway (0) or collocated (-1). **/ int requestId; }; If you look at the comments, you will see these reflected in the documentation for in the online .Ice::Current Slice API Reference Documentation Comments A documentation comment: starts with /** ends with **/ Such a comment can precede any Slice construct, such as a module, interface, structure, operation, and so on. Within a documentationIce 3.4.2 Documentation 157 Copyright © 2011, ZeroC, Inc. comment, you can either start each line with a , or you can leave the beginning of the line blank — can handle either* slice2html convention: Slice /** * * This is a documentation comment for which every line * starts with a '*' character. **/ /** This is a documentation comment without a leading '*' for each line. Either style of comment is fine. **/ The first sentence of the documentation comment for a Slice construct should be a summary sentence. generates an index ofslice2html all Slice constructs; the first sentence of the comments for each Slice construct is ued as a summary in that index. Hyperlinks Any Slice identifier enclosed in square brackets is presented as a hyperlink in code font. For example: Slice /** * An empty [name] denotes a null object. **/ This generates a hyperlink for the markup that points at the definition of the corresponding Slice symbol. (The symbol can denote anyname Slice construct, such as a type, interface, parameter, or structure member.) Explicit Cross-References The directive is recognized by . Where it appears, the generated HTML contains a separate section titled "See Also",@see slice2html followed by a list of Slice identifiers. For example: Slice /** * The object adapter, which is responsible for receiving requests * from endpoints, and for mapping between servants, identities, * and proxies. * * @see Communicator * @see ServantLocator **/ The Slice identifiers are listed in the corresponding "See Also" section as hyperlinks in code font. Markup for Operations There are three directives specifically to document Slice operations: , , and . For example:@param @return @throwsIce 3.4.2 Documentation 158 Copyright © 2011, ZeroC, Inc. Slice /** * Look for an item with the specified * primary and secondary key. * * @param p The primary search key. * * @param s The secondary search key. * * @return The item that matches the specified keys. * * @throws NotFound Raised if no item matches the specified keys. **/ Item findItem(Key p, Key s) throws NotFound; slice2html generates separate "Parameters", "Return Value", and "Exceptions" sections for these directives. Parameters are listed in the same order as they appear in the comments. (For clarity, that order should match the order of declaration of parameters for the corresponding operation.) General HTML Markup A documentation comment can contain any markup that is permitted by HTML in that place. For example, you can create separate paragraphs with and elements:

Slice /** * This is a comment for some Slice construct.

* *

This comment appears in a separate paragraph. **/ Note that you must neither begin a documentation comment with a element nor end it with a element because, in the generated

HTML, documentation comments are already surrounded by and elements.

There are various other ways to create markup — for example, you can use or elements. Please see the
    HTML specification for details. Using slice2html slice2html uses the following syntax: slice2html [options] slice_file... If you have cross-references that span Slice files, you must compile all of the Slice files with a single invocation of .slice2html The command supports the following options: -h, --help Displays a help message. -v, --version Displays the compiler version. -DNAME Defines the preprocessor symbol .NAME -D =NAME DEF Defines the preprocessor symbol with the value .NAME DEFIce 3.4.2 Documentation 159 Copyright © 2011, ZeroC, Inc. -UNAME Undefines the preprocessor symbol { .NAME -IDIR Add the directory to the search path for directives.DIR #include -E Print the preprocessor output on .stdout --output-dir DIR Place the generated files into directory .DIR -d, --debug Print debug information showing the operation of the Slice parser. --ice Permit use of the normally reserved prefix for identifiers. Use this option only when compiling the source code for the Ice runIce time. --underscore Permit use of underscores in Slice identifiers. --hdr FILE Prepend to each generated HTML file (except for ). This allows you to replace the HTML header and otherFILE _sindex.html preamble information with a custom version, so you can connect style sheets to the generated pages. The specified file must include the tag (but need not end with a tag). is expected to contain the string on a line by itself, starting in FILE TITLE column one. slice2html replaces the string with the fully-scoped name of the Slice symbol that is documented on theTITLE corresponding page. --ftr FILE Append to each generated HTML file (except for ). This allows you to add, for example, a custom footer toFILE _sindex.html each generated page. must end with a tag.FILE --indexhdr FILE generates a file that contains a table of contents of all Slice symbols that hyperlink to theslice2html _sindex.html corresponding page. This option allows you to replace the standard header with a custom header, for example, to attach a JavaScript. The specified file must include the tag (but need not end with a tag). The default value is the setting of (if any).--hdr --indexftr FILE Append to the generated FILE sindex.html page. This allows you to add, for example, a custom footer to the table of contents, is must end with a tag. The default value is the setting of (if any).or to invoke a JavaScript. _FILE --ftr --image-dir DIR With this option, looks in the specified directory for images to use for the generated navigation hyperlinks. (Withoutslice2html this option, text links are used instead.) Please see the generated HTML for the names of the various image files. (They can easily be found by looking for elements.)img --logo-url URL Use the specified URL as a hyperlink for the company logo that is added to each page (if is specified). The company--image-dir logo is expected to be in ./logo.gif --search ACTION If this option is specified, the generated pages contain a search box that allows you to connect the generated pages to a search engine. On pressing the "Search" button, the specified is carried out.ACTION --index NUM generates sub-indexes for various Slice symbols. This option controls how many entries must be present before aslice2html sub-index is generated. For example, if is set to 3, a sub-index will be generated only if there are three or more symbols thatNUM appear in that index. The default settings is 1, meaning that a sub-index is always generated. To disable sub-indexes entirely, set to 0.NUM --summary NUM If this option is set, summary sentences that exceed characters generate a warning.NUM See Also Slice API reference HTML specificationIce 3.4.2 Documentation 160 Copyright © 2011, ZeroC, Inc.Ice 3.4.2 Documentation 161 Copyright © 2011, ZeroC, Inc. Slice Keywords The following identifiers are Slice keywords: bool exception interface sequence byte extends local short class false LocalObject string const float long struct dictionary idempotent module throws double implements Object true enum int out void Keywords must be capitalized as shown. See Also Lexical RulesIce 3.4.2 Documentation 162 Copyright © 2011, ZeroC, Inc. Slice Metadata Directives On this page: General Metadata Directives Metadata Directives for C++ Metadata Directives for Java Metadata Directives for C# Metadata Directives for .NET and Mono Metadata Directives for Objective-C Metadata Directives for Python Metadata Directives for Freeze General Metadata Directives ami This directive applies to interfaces, classes, and individual operations. It enable code generation for asynchronous method invocation. This directive applies to the . For the new AMI mapping there is no need for this directive.deprecated AMI mapping amd This directive applies to interfaces, classes, and individual operations. It enables code generation for asynchronous method dispatch. (See the relevant language mapping chapter for details.) deprecated This directive allows you to emit a .deprecation warning for Slice constructs protected This directive applies to data members of classes and changes code generation to make these members protected. See class mapping of the relevant language mapping chapter for more information. UserException This directive applies only to operations on local interfaces. The metadata directive indicates that the operation can throw any user exception, regardless of its specific definition. (This directive is used for the and operations on servant locators, whichlocate finished can throw any user exception.) Metadata Directives for C++ cpp:array and cpp:range:array These directives apply to sequences. They direct the code generator to create zerocopy APIs for .passing sequences as parameters cpp:class This directive applies to . It directs the code generator to create a C++ class (instead of a C++ structure) to represent a Slicestructures structure. cpp:const This directive applies to operations. It directs the code generator to create a pure virtual member function for the .const skeleton class cpp:type:wstringIce 3.4.2 Documentation 163 Copyright © 2011, ZeroC, Inc. This directive applies to data members of type string as well as to containers, such as structures, classes, and exceptions. It changes the from to .default mapping for strings std::string std::wstring cpp:header-ext This global directive allows you to use a other than the default extension.file extension for C++ header files .h cpp:include This global directive allows you inject additional #include directives into the generated code. This is useful for .custom types cpp:virtual This directive applies to classes. If the directive is present and a class has base classes, the generated C++ class derives virtually from its bases; without this directive, slice2cpp generates the class so it derives non-virtually from its bases. This directive is useful if you use Slice classes as servants and want to inherit the implementation of operations in the base class in the derived class. For example: Slice class Base { int baseOp(); }; ["cpp:virtual"] class Derived extends Base { string derivedOp(); }; The metadata directive causes slice2cpp to generate the class definition for using virtual inheritance:Derived C++ class Base : virtual public Ice::Object { // ... }; class Derived : virtual public Base { // ... }; This allows you to reuse the implementation of in the servant for using ladder inheritance:baseOp Derived C++ class BaseI : public virtual Base { Ice::Int baseOp(const Ice::Current&); // ... }; class DerivedI : public virtual Derived, public virtual BaseI { // Re-use inherited baseOp() }; Note that, if you have data member in classes and use virtual inheritance, you need to take care to correctly call base class constructors if you implement your own one-shot constructor. For example:Ice 3.4.2 Documentation 164 Copyright © 2011, ZeroC, Inc. Slice class Base { int baseInt; }; class Derived extends Base { int derivedInt; }; The generated one-shot constructor for initializes both and :Derived baseInt derivedInt C++ Derived::Derived(Ice::Int __ice_baseInt, Ice::Int __ice_derivedInt) : M::Base(__ice_baseInt), derivedInt(__ice_derivedInt) { } If you derive your own class from and add a one-shot constructor to your class, you must explicitly call the constructor of all theDerived base classes, including . Failure to call the constructor will result in being default-constructed instead of getting a definedBase Base Base value. For example: C++ class DerivedI : public virtual Derived { public: DerivedI(int baseInt, int derivedInt, const string& s) : Base(baseInt), Derived(baseInt, derivedInt), _s(s) { } private: string _s; }; This code correctly initializes the member of the part of the class. Note that the following does not work as intended andbaseInt Base leaves the part default-constructed (meaning that is not initialized):Base baseInt C++ class DerivedI : public virtual Derived { public: DerivedI(int baseInt, int derivedInt, const string& s) : Derived(baseInt, derivedInt), _s(s) { // WRONG: Base::baseInt is not initialized. } private: string _s; }; Metadata Directives for Java java:packageIce 3.4.2 Documentation 165 Copyright © 2011, ZeroC, Inc. This global directive instructs the code generator to place the generated classes into a .specific package java:getset This directive applies to data members and structures, classes, and exceptions. It adds accessor and modifier methods ( )JavaBean methods for data members. java:serializable This directive allows you to use Ice to transmit as native objects, without having to define corresponding Sliceserializable Java classes definitions for these classes. java:type This directive allows to use for sequences and dictionaries.custom types Metadata Directives for C# Note that C# (and other Common Language Runtime languages) are also affected by metadata with a prefix. (See clr: Metadata Directives .)for .NET and Mono cs:attribute This directive can be used both as a global directive and as directive for specific Slice constructs. It injects C# attribute definitions into the generated code. (See .)C-Sharp Specific Metadata Directives Metadata Directives for .NET and Mono clr:class This directive applies to Slice structures. It directs the code generator to emit a instead of a structure.C# class clr:collection This directive applies to and and maps them to types that are derived from and sequences dictionaries CollectionBase , respectively.DictionaryBase clr:generic:List, , and clr:generic:LinkedList clr:generic:Queue clr:generic:Stack These directives apply to and map them to the specified sequence type.sequences clr:generic:SortedDictionary This directive applies to and maps them to .dictionaries SortedDictionary clr:generic This directive applies to and allows you map them to custom types.sequences clr:property This directive applies to Slice structures and classes. It directs the code generator to create for data members.C# property definitions clr:serializable This directive allows you to use Ice to transmit , without having to define corresponding Sliceserializable CLR classes as native objects definitions for these classes. Metadata Directives for Objective-CIce 3.4.2 Documentation 166 Copyright © 2011, ZeroC, Inc. objc:prefix This directive applies to modules and changes the to use a specified prefix.default mapping for modules Metadata Directives for Python python:package This global directive instructs the code generator to place the generated code into a .specified Python package python:seq:default, and python:seq:list python:seq:tuple These directives allow you to change the .mapping for Slice sequences Metadata Directives for Freeze freeze:read and freeze:write These directives inform a Freeze evictor whether an operation of an object, so the evictor knows whether it must save anupdates the state object before evicting it. See Also MetadataIce 3.4.2 Documentation 167 Copyright © 2011, ZeroC, Inc. Slice for a Simple File System For this manual, we use a file system application to illustrate various aspects of Ice. Throughout, we progressively improve and modify the application such that it evolves into an application that is realistic and illustrates the architectural and coding aspects of Ice. This allows us to explore the capabilities of the platform to a realistic degree of complexity without overwhelming you with an inordinate amount of detail early on. In this section: File System Application outlines the file system functionality Slice Definitions for the File System develops the data types and interfaces that are required for the file system Complete Definition presents the complete Slice definition for the application. File System Application Our file system application implements a simple hierarchical file system, similar to the file systems we find in Windows or Unix. To keep code examples to manageable size, we ignore many aspects of a real file system, such as ownership, permissions, symbolic links, and a number of other features. However, we build enough functionality to illustrate how you could implement a fully-featured file system, and we pay attention to things such as performance and scalability. In this way, we can create an application that presents us with real-world complexity without getting buried in large amounts of code. Our file system consists of directories and files. Directories are containers that can contain either directories or files, meaning that the file system is hierarchical. A dedicated directory is at the root of the file system. Each directory and file has a name. Files and directories with a common parent directory must have different names (but files and directories with different parent directories can have the same name). In other words, directories form a naming scope, and entries with a single directory must have unique names. Directories allow you to list their contents. For now, we do not have a concept of pathnames, or the creation and destruction of files and directories. Instead, the server provides a fixed number of directories and files. (We will address the creation and destruction of files and directories in .)Object Life Cycle Files can be read and written but, for now, reading and writing always replace the entire contents of a file; it is impossible to read or write only parts of a file. Slice Definitions for the File System Given the very simple requirements we just outlined, we can start designing interfaces for the system. Files and directories have something in common: they have a name and both files and directories can be contained in directories. This suggests a design that uses a base type that provides the common functionality, and derived types that provide the functionality specific to directories and files, as shown: Inheritance Diagram of the File System. The Slice definitions for this look as follows:Ice 3.4.2 Documentation 168 Copyright © 2011, ZeroC, Inc. Slice interface Node { // ... }; interface File extends Node { // ... }; interface Directory extends Node { // ... }; Next, we need to think about what operations should be provided by each interface. Seeing that directories and files have names, we can add an operation to obtain the name of a directory or file to the base interface:Node Slice interface Node { idempotent string name(); }; The interface provides operations to read and write a file. For simplicity, we limit ourselves to text files and we assume that File read operations never fail and that only operations can encounter error conditions. This leads to the following definitions:write Slice exception GenericError { string reason; }; sequence Lines; interface File extends Node { idempotent Lines read(); idempotent void write (Lines text) throws GenericError; }; Note that and are marked idempotent because either operation can safely be invoked with the same parameter value twice in aread write row: the net result of doing so is the same has having (successfully) called the operation only once. The operation can raise an exception of type . The exception contains a single data member, of type write GenericError reason . If a operation fails for some reason (such as running out of file system space), the operation throws a string write GenericError exception, with an explanation of the cause of the failure provided in the data member.reason Directories provide an operation to list their contents. Because directories can contain both directories and files, we take advantage of the polymorphism provided by the base interface:Node Slice sequence NodeSeq; interface Directory extends Node { idempotent NodeSeq list(); }; The sequence contains elements of type . Because is a base interface of both and , the NodeSeq Node* Node Directory File NodeSeq sequence can contain proxies of either type. (Obviously, the receiver of a must down-cast each element to either or NodeSeq File in order to get at the operations provided by the derived interfaces; only the operation in the base interface can beDirectory name Node invoked directly, without doing a down-cast first. Note that, because the elements of are of type (not ), we are usingNodeSeq Node* Node pass-by-reference semantics: the values returned by the operation are proxies that each point to a remote node on the server.listIce 3.4.2 Documentation 169 Copyright © 2011, ZeroC, Inc. These definitions are sufficient to build a simple (but functional) file system. Obviously, there are still some unanswered questions, such as how a client obtains the proxy for the root directory. We will address these questions in the relevant implementation chapter. Complete Definition We wrap our definitions in a module, resulting in the final definition as follows: Slice module Filesystem { interface Node { idempotent string name(); }; exception GenericError { string reason; }; sequence Lines; interface File extends Node { idempotent Lines read(); idempotent void write(Lines text) throws GenericError; }; sequence NodeSeq; interface Directory extends Node { idempotent NodeSeq list(); }; }; See Also Object Life CycleIce 3.4.2 Documentation 170 Copyright © 2011, ZeroC, Inc. C++ Mapping Topics Client-Side Slice-to-C++ Mapping Server-Side Slice-to-C++ Mapping The C++ Utility LibraryIce 3.4.2 Documentation 171 Copyright © 2011, ZeroC, Inc. Client-Side Slice-to-C++ Mapping The client-side Slice-to-C++ mapping defines how Slice data types are translated to C++ types, and how clients invoke operations, pass parameters, and handle errors. Much of the C++ mapping is intuitive. For example, Slice sequences map to STL vectors, so there is essentially nothing new you have to learn in order to use Slice sequences in C++. The rules that make up the C++ mapping are simple and regular. In particular, the mapping is free from the potential pitfalls of memory management: all types are self-managed and automatically clean up when instances go out of scope. This means that you cannot accidentally introduce a memory leak by, for example, ignoring the return value of an operation invocation or forgetting to deallocate memory that was allocated by a called operation. The C++ mapping is fully thread-safe. For example, the for classes is interlocked against parallel access, soreference counting mechanism reference counts cannot be corrupted if a class instance is shared among a number of threads. Obviously, you must still synchronize access to data from different threads. For example, if you have two threads sharing a sequence, you cannot safely have one thread insert into the sequence while another thread is iterating over the sequence. However, you only need to concern yourself with concurrent access to your own data — the Ice run time itself is fully thread safe, and none of the Ice API calls require you to acquire or release a lock before you safely can make the call. Much of what appears in this chapter is reference material. We suggest that you skim the material on the initial reading and refer back to specific sections as needed. However, we recommend that you read at least the mappings for , , and inexceptions interfaces operations detail because these sections cover how to call operations from a client, pass parameters, and handle exceptions. In order to use the C++ mapping, you should need no more than the Slice definition of your application and knowledge of the C++ mapping rules. In particular, looking through the generated header files in order to discern how to use the C++ mapping is likely to be confusing because the header files are not necessarily meant for human consumption and, occasionally, contain various cryptic constructs to deal with operating system and compiler idiosyncrasies. Of course, occasionally, you may want to refer to a header file to confirm a detail of the mapping, but we recommend that you otherwise use the material presented here to see how to write your client-side code. The Ice Namespace All of the APIs for the Ice run time are nested in the namespace, to avoid clashes with definitions for other libraries orIce applications. Some of the contents of the namespace are generated from Slice definitions; other parts of the Ice Ice namespace provide special-purpose definitions that do not have a corresponding Slice definition. We will incrementally cover the contents of the namespace throughout the remainder of the manual.Ice Topics C++ Mapping for Identifiers C++ Mapping for Modules C++ Mapping for Built-In Types C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for Constants C++ Mapping for Exceptions C++ Mapping for Interfaces C++ Mapping for Operations C++ Mapping for Classes Smart Pointers for Classes Asynchronous Method Invocation (AMI) in C++ slice2cpp Command-Line Options Using Slice Checksums in C++ Example of a File System Client in C++Ice 3.4.2 Documentation 172 Copyright © 2011, ZeroC, Inc. C++ Mapping for Identifiers A Slice maps to an identical C++ identifier. For example, the Slice identifier becomes the C++ identifier . There is oneidentifier Clock Clock exception to this rule: if a Slice identifier is the same as a C++ keyword, the corresponding C++ identifier is prefixed with . For_cpp_ example, the Slice identifier is mapped as .while _cpp_while A single Slice identifier often results in several C++ identifiers. For example, for a Slice interface named , the generated C++ code usesFoo the identifiers and (among others). If the interface has the name , the generated identifiers are and Foo FooPrx while _cpp_while ( ), that is, the prefix is applied only to those generated identifiers that actually require it.whilePrx not _cpp_whilePrx You should try to as much as possible.avoid such identifiers See Also Lexical Rules C++ Mapping for Modules C++ Mapping for Built-In Types C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for Constants C++ Mapping for ExceptionsIce 3.4.2 Documentation 173 Copyright © 2011, ZeroC, Inc. C++ Mapping for Modules A Slice maps to a C++ namespace. The mapping preserves the nesting of the Slice definitions. For example:module Slice module M1 { module M2 { // ... }; // ... }; // ... module M1 { // Reopen M1 // ... }; This definition maps to the corresponding C++ definition: C++ namespace M1 { namespace M2 { // ... } // ... } // ... namespace M1 { // Reopen M1 // ... } If a Slice module is reopened, the corresponding C++ namespace is reopened as well. See Also Modules C++ Mapping for Identifiers C++ Mapping for Built-In Types C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for Constants C++ Mapping for ExceptionsIce 3.4.2 Documentation 174 Copyright © 2011, ZeroC, Inc. C++ Mapping for Built-In Types On this page: Mapping of Slice Built-In Types to C++ Types Alternative String Mapping for C++ Mapping of Slice Built-In Types to C++ Types The Slice are mapped to C++ types as shown in this table:built-in types Slice C++ bool bool byte Ice::Byte short Ice::Short int Ice::Int long Ice::Long float Ice::Float double Ice::Double string std::string Slice and map to C++ and . The remaining built-in Slice types map to C++ type definitions instead of C++bool string bool std::string native types. This allows the Ice run time to provide a definition as appropriate for each target architecture. (For example, mightIce::Int be defined as on one architecture and as on another.)long int Note that is a typedef for . This guarantees that byte values are always in the range 0..255.Ice::Byte unsigned char All the basic types are guaranteed to be distinct C++ types, that is, you can safely overload functions that differ in only the types listed in the table above. Alternative String Mapping for C++ You can use a metadata directive, , to map strings to C++ . This is useful for applications that use["cpp:type:wstring"] std::wstring languages with alphabets that cannot be represented in 8?bit characters. The metadata directive can be applied to any Slice construct. For containers (such as modules, interfaces, or structures), the metadata directive applies to all strings within the container. A corresponding metadata directive, , can be used to selectively override the mapping defined by the enclosing container. For["cpp:type:string"] example:Ice 3.4.2 Documentation 175 Copyright © 2011, ZeroC, Inc. Slice ["cpp:type:wstring"] struct S1 { string x; // Maps to std::wstring ["cpp:type:wstring"] string y; // Maps to std::wstring ["cpp:type:string"] string z; // Maps to std::string }; struct S2 { string x; // Maps to std::string ["cpp:type:string"] string y; // Maps to std::string ["cpp:type:wstring"] string z; // Maps to std::wstring }; With these metadata directives, the strings are mapped as indicated by the comments. By default, narrow strings are encoded as UTF?8, and wide strings use Unicode in an encoding that is appropriate for the platform on which the application executes. You can override the encoding for narrow and wide strings by registering a with the Ice run time.string converter See Also Basic Types C++ Mapping for Identifiers C++ Mapping for Modules C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for Constants C++ Mapping for Exceptions C++ Strings and Character EncodingIce 3.4.2 Documentation 176 Copyright © 2011, ZeroC, Inc. C++ Mapping for Enumerations A Slice maps to the corresponding enumeration in C++. For example:enumeration Slice enum Fruit { Apple, Pear, Orange }; Not surprisingly, the generated C++ definition is identical: C++ enum Fruit { Apple, Pear, Orange }; See Also Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for DictionariesIce 3.4.2 Documentation 177 Copyright © 2011, ZeroC, Inc. C++ Mapping for Structures A Slice maps to a C++ structure by default. In addition, you can use a metadata directive to map structures to C++ .structure classes On this page: Default Mapping for Structures in C++ Class Mapping for Structures in C++ Default Constructors for Structures in C++ Default Mapping for Structures in C++ Slice structures map to C++ structures with the same name. For each Slice data member, the C++ structure contains a public data member. For example, here is our structure once more:Employee Slice struct Employee { long number; string firstName; string lastName; }; The Slice-to-C++ compiler generates the following definition for this structure: C++ struct Employee { Ice::Long number; std::string firstName; std::string lastName; bool operator==(const Employee&) const; bool operator!=(const Employee&) const; bool operator<(const Employee&) const; bool operator<=(const Employee&) const; bool operator>(const Employee&) const; bool operator>=(const Employee&) const; }; For each data member in the Slice definition, the C++ structure contains a corresponding public data member of the same name. Constructors are intentionally omitted so that the C++ structure qualifies as a (POD).plain old datatype Note that the structure also contains comparison operators. These operators have the following behavior: operator== Two structures are equal if (recursively), all its members are equal. operator!= Two structures are not equal if (recursively), one or more of its members are not equal. operator< operator<= operator> operator>= The comparison operators treat the members of a structure as sort order criteria: the first member is considered the first criterion, the second member the second criterion, and so on. Assuming that we have two structures, and , this means thatEmployee s1 s2 the generated code uses the following algorithm to compare and :s1 s2Ice 3.4.2 Documentation 178 Copyright © 2011, ZeroC, Inc. C++ bool Employee::operator<(const Employee& rhs) const { if (this == &rhs) // Short?cut self?comparison return false; // Compare first members // if (number < rhs.number) return true; else if (rhs.number < number) return false; // First members are equal, compare second members // if (firstName < rhs.firstName) return true; else if (rhs.firstName < firstName) return false; // Second members are equal, compare third members // if (lastName < rhs.lastName) return true; else if (rhs.lastName < lastName) return false; // All members are equal, so return false return false; } The comparison operators are provided to allow the use of structures as the key type of , which are mapped to Slice dictionaries in C++.std::map Note that copy construction and assignment always have deep-copy semantics. You can freely assign structures or structure members to each other without having to worry about memory management. The following code fragment illustrates both comparison and deep-copy semantics: C++ Employee e1, e2; e1.firstName = "Bjarne"; e1.lastName = "Stroustrup"; e2 = e1; // Deep copy assert(e1 == e2); e2.firstName = "Andrew"; // Deep copy e2.lastName = "Koenig"; // Deep copy assert(e2 < e1); Because strings are mapped to , there are no memory management issues in this code and structure assignment and copyingstd::string work as expected. (The default member-wise copy constructor and assignment operator generated by the C++ compiler do the right thing.) Class Mapping for Structures in C++ Occasionally, the mapping of Slice structures to C++ structures can be inefficient. For example, you may need to pass structures around in your application, but want to avoid having to make expensive copies of the structures. (This overhead becomes noticeable for structures with many complex data members, such as sequences or strings.) Of course, you could pass the structures by const reference, but that can create its own share of problems, such as tracking the life time of the structures to avoid ending up with dangling references. For this reason, you can enable an alternate mapping that maps Slice structures to C++ classes. Classes (as opposed to structures) are reference-counted. Because the Ice C++ mapping provides , you can keep references to a class instance in manysmart pointers for classes places in the code without having to worry about either expensive copying or life time issues.Ice 3.4.2 Documentation 179 Copyright © 2011, ZeroC, Inc. The alternate mapping is enabled by a metadata directive, . Here is our Employee structure once again, but this time with["cpp:class"] the additional metadata directive: Slice ["cpp:class"] struct Employee { long number; string firstName; string lastName; }; Here is the generated class: C++ class Employee : public IceUtil::Shared { public: Employee() {} Employee(::Ice::Long, const ::std::string&, const ::std::string&); ::Ice::Long number; ::std::string firstName; ::std::string lastName; bool operator==(const Employee&) const; bool operator!=(const Employee&) const; bool operator<(const Employee&) const; bool operator<=(const Employee&) const; bool operator>(const Employee&) const; bool operator>=(const Employee&) const; }; Note that the generated class, apart from a default constructor, has a constructor that accepts one argument for each member of the structure. This allows you to instantiate and initialize the class in a single statement (instead of having to first instantiate the class and then assign to its members). As for the default structure mapping, the class contains one public data member for each data member of the corresponding Slice structure. The comparison operators behave as for the default structure mapping. You can learn how to , and how to access them via , in the sections describing the mapping for Sliceinstantiate classes smart pointers classes — the API described there applies equally to Slice structures that are mapped to classes. Default Constructors for Structures in C++ Structures have an implicit default constructor that default-constructs each data member. Members having a complex type, such as strings, sequences, and dictionaries, are initialized by their own default constructor. However, the default constructor performs no initialization for members having one of the simple built?in types boolean, integer, floating point, or enumeration. For such a member, it is not safe to assume that the member has a reasonable default value. This is especially true for enumerated types as the member's default value may be outside the legal range for the enumeration, in which case an exception will occur during marshaling unless the member is explicitly set to a legal value. To ensure that data members of primitive types are initialized to reasonable values, you can declare default values in your .Slice definition The default constructor initializes each of these data members to its declared value. If you declare a default value for at least one member of a structure, or use the class mapping for the structure, the Slice compiler also generates a second constructor. This constructor has one parameter for each data member, allowing you to construct and initializeone-shot an instance in a single statement (instead of first having to construct the instance and then assign to its members). See Also Structures C++ Mapping for Enumerations C++ Mapping for SequencesIce 3.4.2 Documentation 180 Copyright © 2011, ZeroC, Inc. C++ Mapping for DictionariesIce 3.4.2 Documentation 181 Copyright © 2011, ZeroC, Inc. C++ Mapping for Sequences On this page: Default Sequence Mapping in C++ Custom Sequence Mapping in C++ STL Container Mapping for Sequences Array Mapping for Sequences in C++ Range Mapping for Sequences in C++ Default Sequence Mapping in C++ Here is the definition of our sequence once more:FruitPlatter Slice sequence FruitPlatter; The Slice compiler generates the following definition for the sequence:FruitPlatter C++ typedef std::vector FruitPlatter; As you can see, the sequence simply maps to an STL vector, so you can use the sequence like any other STL vector. For example: C++ // Make a small platter with one Apple and one Orange // FruitPlatter p; p.push_back(Apple); p.push_back(Orange); As you would expect, you can use all the usual STL iterators and algorithms with this vector. Custom Sequence Mapping in C++ In addition to the default mapping of sequences to vectors, Ice supports three additional custom mappings for sequences. STL Container Mapping for Sequences You can override the default mapping of Slice sequences to C++ vectors with a metadata directive, for example: Slice [["cpp:include:list"]] module Food { enum Fruit { Apple, Pear, Orange }; ["cpp:type:std::list< ::Food::Fruit>"] sequence FruitPlatter; }; With this metadata directive, the sequence now maps to a C++ :std::listIce 3.4.2 Documentation 182 Copyright © 2011, ZeroC, Inc. C++ #include namespace Food { typedef std::list< Food::Fruit> FruitPlatter; // ... } The metadata directive must be applied to a sequence definition; anything following the prefix is taken to be thecpp:type cpp:type: name of the type. For example, we could use . In that case, the compiler would use a["cpp:type:::std::list< ::Food::Fruit>"] fully-qualified name to define the type: C++ typedef ::std::list< ::Food::Fruit> FruitPlatter; Note that the code generator inserts whatever string you specify following the prefix literally into the generated code. Thiscpp:type: means that, to avoid C++ compilation failures due to unknown symbols, you should use a qualified name for the type. Also note that, to avoid compilation errors in the generated code, you must instruct the compiler to generate an appropriate include directive with the global metadata directive. This causes the compiler to add the linecpp:include C++ #include to the generated header file. Instead of , you can specify a type of your own as the sequence type, for example:std::list Slice [["cpp:include:FruitBowl.h"]] module Food { enum Fruit { Apple, Pear, Orange }; ["cpp:type:FruitBowl"] sequence FruitPlatter; }; With these metadata directives, the compiler will use a C++ type as the sequence type, and add an include directive for theFruitBowl header file to the generated code.FruitBowl.h You can use any class of your choice as a sequence type, but the class must meet certain requirements. ( , , and vector list deque happen to meet these requirements.) The class must have a default constructor and a single-argument constructor that takes the size of the sequence as an argument of unsigned integral type. The class must have a copy constructor. The class must provide a member function that returns the number elements in the sequence as an unsigned integral type.size The class must provide a member function that swaps the contents of the sequence with another sequence of the same type.swap The class must define and types and must provide and member functions with the usualiterator const_iterator begin end semantics; the iterators must be comparable for equality and inequality. Less formally, this means that if the class looks like a , , or with respect to these points, you can use it as a customvector list deque sequence implementation.Ice 3.4.2 Documentation 183 Copyright © 2011, ZeroC, Inc. In addition to modifying the type of a sequence itself, you can also modify the mapping for particular . Forreturn values or parameters example: Slice [["cpp:include:list"]] [["cpp:include:deque"]] module Food { enum Fruit { Apple, Pear, Orange }; sequence FruitPlatter; interface Market { ["cpp:type:list< ::Food::Fruit>"] FruitPlatter barter(["cpp:type:deque< ::Food::Fruit>"] FruitPlatter offer); }; }; With this definition, the default mapping of to a C++ still applies but the return value of is mapped as a FruitPlatter vector barter , and the parameter is mapped as a .list offer deque Array Mapping for Sequences in C++ The array mapping for sequences applies to and to and operations. For example:input parameters out parameters of AMI AMD Slice interface File { void write(["cpp:array"] Ice::ByteSeq contents); }; The metadata directive instructs the compiler to map the parameter to a pair of pointers. With this directive, the cpp:array contents method on the proxy has the following signature:write C++ void write(const std::pair& contents); To pass a byte sequence to the server, you pass a pair of pointers; the first pointer points at the beginning of the sequence, and the second pointer points one element past the end of the sequence. Similarly, for the server side, the method on the skeleton has the following signature:write C++ virtual void write(const ::std::pair&, const ::Ice::Current& = ::Ice::Current()) = 0; The passed pointers denote the beginning and end of the sequence as a range (that is, they use the usual STL semantics[first, last) for iterators). The array mapping is useful to achieve zero-copy passing of sequences. The pointers point directly into the server-side transport buffer; this allows the server-side run time to avoid creating a to pass to the operation implementation, thereby avoiding both allocating memoryvector for the sequence and copying its contents into that memory.Ice 3.4.2 Documentation 184 Copyright © 2011, ZeroC, Inc. You can use the array mapping for any sequence type. However, it provides a performance advantage only for byte sequences (on all platforms) and for sequences of integral or floating point types (x86 platforms only). The called operation in the server must not store a pointer into the passed sequence because the transport buffer into which the pointer points is deallocated as soon as the operation completes. Range Mapping for Sequences in C++ The range mapping for sequences is similar to the array mapping and exists for the same purpose, namely, to enable zero-copy of sequence parameters: Slice interface File { void write(["cpp:range"] Ice::ByteSeq contents); }; The metadata directive instructs the compiler to map the parameter to a pair of . With thiscpp:range contents const_iterator directive, the method on the proxy has the following signature:write C++ void write(const std::pair& contents); Similarly, for the server side, the method on the skeleton has the following signature:write C++ virtual void write( const ::std::pair<::Ice::ByteSeq::const_iterator, ::Ice::ByteSeq::const_iterator>&, const ::Ice::Current& = ::Ice::Current()) = 0; The passed iterators denote the beginning and end of the sequence as a range (that is, they use the usual STL semantics[first, last) for iterators). The motivation for the range mapping is the same as for the array mapping: the passed iterators point directly into the server-side transport buffer and so avoid the need to create a temporary to pass to the operation.vector As for the array mapping, the range mapping can be used with any sequence type, but offers a performance advantage only for byte sequences (on all platforms) and for sequences of integral type (x86 platforms only). The operation must not store an iterator into the passed sequence because the transport buffer into which the iterator points is deallocated as soon as the operation completes. You can optionally add a type name to the metadata directive, for example:cpp:range Slice interface File { void write(["cpp:range:std::deque"] Ice::ByteSeq contents); }; This instructs the compiler to generate a pair of for the specified type:const_iteratorIce 3.4.2 Documentation 185 Copyright © 2011, ZeroC, Inc. C++ virtual void write( const ::std::pair::const_iterator, std::deque::const_iterator>&, const ::Ice::Current& = ::Ice::Current()) = 0; This is useful if you want to combine the range mapping with a custom sequence type that behaves like an STL container. See Also Sequences Asynchronous Method Dispatch (AMD) in C++ C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Dictionaries C++ Mapping for OperationsIce 3.4.2 Documentation 186 Copyright © 2011, ZeroC, Inc. C++ Mapping for Dictionaries Here is the definition of our once more:EmployeeMap Slice dictionary EmployeeMap; The following code is generated for this definition: C++ typedef std::map EmployeeMap; Again, there are no surprises here: a Slice dictionary simply maps to an STL . As a result, you can use the dictionary like any other STL map , for example:map C++ EmployeeMap em; Employee e; e.number = 42; e.firstName = "Stan"; e.lastName = "Lippman"; em[e.number] = e; e.number = 77; e.firstName = "Herb"; e.lastName = "Sutter"; em[e.number] = e; All the usual STL iterators and algorithms work with this map just as well as with any other STL container. See Also Dictionaries C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for SequencesIce 3.4.2 Documentation 187 Copyright © 2011, ZeroC, Inc. C++ Mapping for Constants Slice definitions map to corresponding C++ constant definitions. For example:constant Slice const bool AppendByDefault = true; const byte LowerNibble = 0x0f; const string Advice = "Don't Panic!"; const short TheAnswer = 42; const double PI = 3.1416; enum Fruit { Apple, Pear, Orange }; const Fruit FavoriteFruit = Pear; Here are the generated definitions for these constants: C++ const bool AppendByDefault = true; const Ice::Byte LowerNibble = 15; const std::string Advice = "Don't Panic!"; const Ice::Short TheAnswer = 42; const Ice::Double PI = 3.1416; enum Fruit { Apple, Pear, Orange }; const Fruit FavoriteFruit = Pear; All constants are initialized directly in the header file, so they are compile-time constants and can be used in contexts where a compile-time constant expression is required, such as to dimension an array or as the label of a statement.case switch See Also Constants and Literals C++ Mapping for Identifiers C++ Mapping for Modules C++ Mapping for Built-In Types C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for ExceptionsIce 3.4.2 Documentation 188 Copyright © 2011, ZeroC, Inc. C++ Mapping for Exceptions On this page: C++ Mapping for User Exceptions C++ Default Constructors for Exceptions C++ Mapping for Run-Time Exceptions C++ Mapping for User Exceptions Here is a fragment of the once more:Slice definition for our world time server Slice exception GenericError { string reason; }; exception BadTimeVal extends GenericError {}; exception BadZoneName extends GenericError {}; These exception definitions map as follows: C++ class GenericError: public Ice::UserException { public: std::string reason; GenericError() {} explicit GenericError(const string&); virtual const std::string& ice_name() const; virtual Ice::Exception* ice_clone() const; virtual void ice_throw() const; // Other member functions here... }; class BadTimeVal: public GenericError { public: BadTimeVal() {} explicit BadTimeVal(const string&); virtual const std::string& ice_name() const; virtual Ice::Exception* ice_clone() const; virtual void ice_throw() const; // Other member functions here... }; class BadZoneName: public GenericError { public: BadZoneName() {} explicit BadZoneName(const string&); virtual const std::string& ice_name() const; virtual Ice::Exception* ice_clone() const; virtual void ice_throw() const; }; Each Slice exception is mapped to a C++ class with the same name. For each exception member, the corresponding class contains a public data member. (Since and do not have members, the generated classes for these exceptions also do not haveBadTimeVal BadZoneName members.)Ice 3.4.2 Documentation 189 Copyright © 2011, ZeroC, Inc. The inheritance structure of the Slice exceptions is preserved for the generated classes, so and inherit from BadTimeVal BadZoneName .GenericError Each exception has three additional member functions: ice_name As the name suggests, this member function returns the name of the exception. For example, if you call the memberice_name function of a exception, it (not surprisingly) returns the string . The member function isBadZoneName "BadZoneName" ice_name useful if you catch exceptions generically and want to produce a more meaningful diagnostic, for example: C++ try { // ... } catch (const GenericError& e) { cerr << "Caught an exception: " << e.ice_name() << endl; } If an exception is raised, this code prints the name of the actual exception ( or ) because the exceptionBadTimeVal BadZoneName is being caught by reference (to avoid slicing). ice_clone This member function allows you to polymorphically clone an exception. For example: C++ try { // ... } catch (const Ice::UserException& e) { Ice::UserException* copy = e.clone(); } ice_clone is useful if you need to make a copy of an exception without knowing its precise run-time type. This allows you to remember the exception and throw it later by calling .ice_throw ice_throw allows you to throw an exception without knowing its precise run-time type. It is implemented as:ice_throw C++ void GenericError::ice_throw() const { throw *this; } You can call to throw an exception that you previously cloned with .ice_throw ice_clone Each exception has a default constructor. This constructor performs memberwise initialization; for simple built?in types, such as integers, the constructor performs no initialization, whereas complex types such as strings, sequences, and dictionaries are initialized by their respective default constructors. An exception also has a second constructor that accepts one argument for each exception member. This constructor allows you to instantiate and initialize an exception in a single statement, instead of having to first instantiate the exception and then assign to its members. For derived exceptions, the constructor accepts one argument for each base exception member, plus one argument for each derived exception member, in base-to-derived order. Note that the generated exception classes contain other member functions that are not shown here. However, those member functions are internal to the C++ mapping and are not meant to be called by application code. All user exceptions ultimately inherit from . In turn, inherits from (whichIce::UserException Ice::UserException Ice::Exception is an alias for ):IceUtil::ExceptionIce 3.4.2 Documentation 190 Copyright © 2011, ZeroC, Inc. C++ namespace IceUtil { class Exception { virtual const std::string& ice_name() const; Exception* ice_clone() const; void ice_throw() const; virtual void ice_print(std::ostream&) const; // ... }; std::ostream& operator<<(std::ostream&, const Exception&); // ... } namespace Ice { typedef IceUtil::Exception Exception; class UserException: public Exception { public: virtual const std::string& ice_name() const = 0; // ... }; } Ice::Exception forms the root of the exception inheritance tree. Apart from the usual , , and memberice_name ice_clone ice_throw functions, it contains the member functions. prints the name of the exception. For example, calling onice_print ice_print ice_print a exception prints:BadTimeVal BadTimeVal To make printing more convenient, is overloaded for , so you can also write:operator<< Ice::Exception C++ try { // ... } catch (const Ice::Exception& e) { cerr << e << endl; } This produces the same output because calls internally.operator<< ice_print For Ice run time exceptions, also shows the file name and line number at which the exception was thrown.ice_print C++ Default Constructors for Exceptions Exceptions have a default constructor that default-constructs each data member. Members having a complex type, such as strings, sequences, and dictionaries, are initialized by their own default constructor. However, the default constructor performs no initialization for members having one of the simple built-in types boolean, integer, floating point, or enumeration. For such a member, it is not safe to assume that the member has a reasonable default value. This is especially true for enumerated types as the member's default value may be outside the legal range for the enumeration, in which case an exception will occur during marshaling unless the member is explicitly set to a legal value. To ensure that data members of primitive types are initialized to reasonable values, you can declare in your Slice definition.default values The default constructor initializes each of these data members to its declared value. Exceptions also have a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assign to its members). For derived exceptions, this constructor has one parameter for each of the base class's data members, plus one parameter for each of the derived class's data members, in base-to-derived order. C++ Mapping for Run-Time ExceptionsIce 3.4.2 Documentation 191 Copyright © 2011, ZeroC, Inc. The Ice run time throws run-time exceptions for a number of pre-defined error conditions. All run-time exceptions directly or indirectly derive from (which, in turn, derives from ). has the usual member functions: Ice::LocalException Ice::Exception Ice::LocalException , , , and (inherited from ), , , and .ice_name ice_clone ice_throw Ice::Exception ice_print ice_file ice_line Recall the for user and run-time exceptions. By catching exceptions at the appropriate point in the hierarchy, you caninheritance diagram handle exceptions according to the category of error they indicate: Ice::Exception This is the root of the complete inheritance tree. Catching catches both user and run-time exceptions.Ice::Exception Ice::UserException This is the root exception for all user exceptions. Catching catches all user exceptions (but not run-timeIce::UserException exceptions). Ice::LocalException This is the root exception for all run-time exceptions. Catching catches all run-time exceptions (but notIce::LocalException user exceptions). Ice::TimeoutException This is the base exception for both operation-invocation and connection-establishment timeouts. Ice::ConnectTimeoutException This exception is raised when the initial attempt to establish a connection to a server times out. For example, a can be handled as , , ConnectTimeoutException ConnectTimeoutException TimeoutException , or .LocalException Exception You will probably have little need to catch run-time exceptions as their most-derived type and instead catch them as ; theLocalException fine-grained error handling offered by the remainder of the hierarchy is of interest mainly in the implementation of the Ice run time. Exceptions to this rule are the exceptions related to and life cycles, which you may want to catch explicitly. These exceptions arefacet object and , respectively.FacetNotExistException ObjectNotExistException See Also User Exceptions Run-Time Exceptions C++ Mapping for Identifiers C++ Mapping for Modules C++ Mapping for Built-In Types C++ Mapping for Enumerations C++ Mapping for Structures C++ Mapping for Sequences C++ Mapping for Dictionaries C++ Mapping for Constants Facets and Versioning Object Life CycleIce 3.4.2 Documentation 192 Copyright © 2011, ZeroC, Inc. C++ Mapping for Interfaces The mapping of Slice revolves around the idea that, to invoke a remote operation, you call a member function on a local classinterfaces instance that is a for the remote object. This makes the mapping easy and intuitive to use because making a remote procedure call isproxy no different from making a local procedure call (apart from error semantics). On this page: Proxy Classes and Proxy Handles Inheritance from Ice::Object Proxy Handles and ProxyType PointerType Methods on Proxy Handles Default constructor Copy constructor Assignment operator Checked cast Unchecked cast Stream insertion and stringification Using Proxy Methods in C++ Object Identity and Proxy Comparison in C++ Proxy Classes and Proxy Handles On the client side, a Slice interface maps to a class with member functions that correspond to the operations on that interface. Consider the following simple interface: Slice module M { interface Simple { void op(); }; }; The Slice compiler generates the following definitions for use by the client:Ice 3.4.2 Documentation 193 Copyright © 2011, ZeroC, Inc. C++ namespace IceProxy { namespace M { class Simple; } } namespace M { class Simple; typedef IceInternal::ProxyHandle< ::IceProxy::M::Simple> SimplePrx; typedef IceInternal::Handle< ::M::Simple> SimplePtr; } namespace IceProxy { namespace M { class Simple : public virtual IceProxy::Ice::Object { public: typedef ::M::SimplePrx ProxyType; typedef ::M::SimplePtr PointerType; void op(); void op(const Ice::Context&); // ... }; }; } As you can see, the compiler generates a in the namespace, as well as a handle .proxy class Simple IceProxy::M proxy M::SimplePrx In general, for a module , the generated names are and .M ::IceProxy::M:: ::M::Prx In the client's address space, an instance of is the local ambassador for a remote instance of the interfaceIceProxy::M::Simple Simple in a server and is known as a . All the details about the server-side object, such as its address, what protocol to use, andproxy class instance its object identity are encapsulated in that instance. Inheritance from Ice::Object Simple inherits from , reflecting the fact that all Ice interfaces implicitly inherit from . For eachIceProxy::Ice::Object Ice::Object operation in the interface, the proxy class has two overloaded member functions of the same name. For the preceding example, we find that the operation has been mapped to two member functions .op op One of the overloaded member functions has a trailing parameter of type . This parameter is for use by the Ice run time toIce::Context store information about how to deliver a request; normally, you do not need to supply a value here and can pretend that the trailing parameter does not exist. (The parameter is also used by .)IceStorm Proxy Handles Client-side application code never manipulates proxy class instances directly. In fact, you are not allowed to instantiate a proxy class directly. The following code will not compile because is an abstract base class with a protected constructor and destructor:Ice::Object C++ IceProxy::M::Simple s; // Compile?time error! Proxy instances are always instantiated on behalf of the client by the Ice run time, so client code never has any need to instantiate a proxy directly. When the client receives a proxy from the run time, it is given a to the proxy, of type (proxy handle Prx for the preceding example). The client accesses the proxy via its proxy handle; the handle takes care of forwarding operationSimplePrx invocations to its underlying proxy, as well as reference-counting the proxy. This means that no memory-management issues can arise: deallocation of a proxy is automatic and happens once the last handle to the proxy disappears (goes out of scope). Because the application code always uses proxy handles and never touches the proxy class directly, we usually use the term proxy to denote both proxy handle and proxy class. This reflects the fact that, in actual use, the proxy handle looks and feels like the underlying proxy class instance. If the distinction is important, we use the terms , , and .proxy class proxy class instance proxy handleIce 3.4.2 Documentation 194 Copyright © 2011, ZeroC, Inc. ProxyType and PointerType The generated proxy class contains type definitions for and . These are provided so you can refer to the proxyProxyType PointerType type and in template definitions without having to resort to preprocessor trickery, for example:smart pointer type C++ template class ProxyWrapper { public: T::ProxyType proxy() const; // ... }; Methods on Proxy Handles As we saw for the preceding example, the handle is actually a template of type that takes the proxy classIceInternal::ProxyHandle as the template parameter. This template has the usual default constructor, copy constructor, and assignment operator. Default constructor You can default-construct a proxy handle. The default constructor creates a proxy that points nowhere (that is, points at no object at all). If you invoke an operation on such a null proxy, you get an :IceUtil::NullHandleException C++ try { SimplePrx s; // Default?constructed proxy s?>op(); // Call via nil proxy assert(0); // Can't get here } catch (const IceUtil::NullHandleException&) { cout << "As expected, got a NullHandleException" << endl; } Copy constructor The copy constructor ensures that you can construct a proxy handle from another proxy handle. Internally, this increments a reference count on the proxy; the destructor decrements the reference count again and, once the count drops to zero, deallocates the underlying proxy class instance. That way, memory leaks are avoided: C++ { // Enter new scope SimplePrx s1 = ...; // Get a proxy from somewhere SimplePrx s2(s1); // Copy?construct s2 assert(s1 == s2); // Assertion passes } // Leave scope; s1, s2, and the // underlying proxy instance // are deallocated Note the assertion in this example: .proxy handles support comparison Assignment operator You can freely assign proxy handles to each other. The handle implementation ensures that the appropriate memory-management activities take place. Self-assignment is safe and you do not have to guard against it:Ice 3.4.2 Documentation 195 Copyright © 2011, ZeroC, Inc. C++ SimplePrx s1 = ...; // Get a proxy from somewhere SimplePrx s2; // s2 is nil s2 = s1; // both point at the same object s1 = 0; // s1 is nil s2 = 0; // s2 is nil Widening assignments work implicitly. For example, if we have two interfaces, and , we can widen a to a Base Derived DerivedPrx implicitly:BasePrx C++ BasePrx base; DerivedPrx derived; base = derived; // Fine, no problem derived = base; // Compile?time error Implicit narrowing conversions result in a compile error, so the usual C++ semantics are preserved: you can always assign a derived type to a base type, but not vice versa. Checked cast Proxy handles provide a method:checkedCast C++ namespace IceInternal { template class ProxyHandle : public IceUtil::HandleBase { public: template static ProxyHandle checkedCast(const ProxyHandle& r); template static ProxyHandle checkedCast(const ProxyHandle& r, const ::Ice::Context& c); // ... }; } A checked cast has the same function for proxies as a C++ has for pointers: it allows you to assign a base proxy to adynamic_cast derived proxy. If the base proxy's actual run-time type is compatible with the derived proxy's static type, the assignment succeeds and, after the assignment, the derived proxy denotes the same object as the base proxy. Otherwise, if the base proxy's run-time type is incompatible with the derived proxy's static type, the derived proxy is set to null. Here is an example to illustrate this: C++ BasePrx base = ...; // Initialize base proxy DerivedPrx derived; derived = DerivedPrx::checkedCast(base); if (derived) { // Base has run?time type Derived, // use derived... } else { // Base has some other, unrelated type } The expression tests whether points at an object of type (or an object with a typeDerivedPrx::checkedCast(base) base Derived that is derived from ). If so, the cast succeeds and is set to point at the same object as . Otherwise, the cast failsDerived derived base and is set to the null proxy.derivedIce 3.4.2 Documentation 196 Copyright © 2011, ZeroC, Inc. Note that is a static member function so, to do a down-cast, you always use the syntax checkedCast .Prx::checkedCast Also note that you can use proxies in boolean contexts. For example, returns true if the .if (proxy) proxy is not null A typically results in a remote message to the server.The message effectively asks the server "is the object denoted by thischeckedCast reference of typeDerived?" In some cases, the Ice run time can optimize the cast and avoid sending a message. However, the optimization applies only in narrowly-defined circumstances, so you cannot rely on a not sending a message.checkedCast The reply from the server is communicated to the application code in form of a successful (non-null) or unsuccessful (null) result. Sending a remote message is necessary because, as a rule, there is no way for the client to find out what the actual run-time type of a proxy is without confirmation from the server. (For example, the server may replace the implementation of the object for an existing proxy with a more derived one.) This means that you have to be prepared for a to fail. For example, if the server is not running, you will receivecheckedCast a if the server is running, but the object denoted by the proxy no longer exists, you will receive an ConnectFailedException; .ObjectNotExistException Unchecked cast In some cases, it is known that an object supports a more derived interface than the static type of its proxy. For such cases, you can use an unchecked down-cast: C++ namespace IceInternal { template class ProxyHandle : public IceUtil::HandleBase { public: template static ProxyHandle uncheckedCast(const ProxyHandle& r); // ... }; } An provides a down-cast consulting the server as to the actual run-time type of the object, for example:uncheckedCast without C++ BasePrx base = ...; // Initialize to point at a Derived DerivedPrx derived; derived = DerivedPrx::uncheckedCast(base); // Use derived... You should use an only if you are certain that the proxy indeed supports the more derived type: an , asuncheckedCast uncheckedCast the name implies, is not checked in any way; it does not contact the object in the server and, if it fails, it does not return null. (An unchecked cast is implemented internally like a C++ , no checks of any kind are made). If you use the proxy resulting from an incorrect static_cast to invoke an operation, the behavior is undefined. Most likely, you will receive an , but,uncheckedCast OperationNotExistException depending on the circumstances, the Ice run time may also report an exception indicating that unmarshaling has failed, or even silently return garbage results. Despite its dangers, is still useful because it avoids the cost of sending a message to the server. And, particularly during uncheckedCast , it is common to receive a proxy of static type , but with a known run-time type. In such cases, an initialization Ice::Object saves the overhead of sending a remote message.uncheckedCast Stream insertion and stringification For convenience, proxy handles also support insertion of a proxy into a stream, for example:Ice 3.4.2 Documentation 197 Copyright © 2011, ZeroC, Inc. C++ Ice::ObjectPrx p = ...; cout << p << endl; This code is equivalent to writing: C++ Ice::ObjectPrx p = ...; cout << p?>ice_toString() << endl; Either code prints the . You could also achieve the same thing by writing:stringified proxy C++ Ice::ObjectPrx p = ...; cout << communicator?>proxyToString(p) << endl; The advantage of using the member function instead of is that you do not need to have the communicatorice_toString proxyToString available at the point of call. Using Proxy Methods in C++ The base proxy class supports a variety of . Since proxies are immutable, each of theseObjectPrx methods for customizing a proxy "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second timeout as shown below: C++ Ice::ObjectPrx proxy = communicator->stringToProxy(...); proxy = proxy->ice_timeout(10000); A factory method returns a new proxy object if the requested modification differs from the current proxy, otherwise it returns the current proxy. With few exceptions, factory methods return a proxy of the same type as the current proxy, therefore it is generally not necessary to repeat a down-cast after using a factory method. The example below demonstrates these semantics: C++ Ice::ObjectPrx base = communicator->stringToProxy(...); HelloPrx hello = HelloPrx::checkedCast(base); hello = hello->ice_timeout(10000); // Type is preserved hello->sayHello(); The only exceptions are the factory methods and . Calls to either of these methods may produce a proxy for anice_facet ice_identity object of an unrelated type, therefore they return a base proxy that you must subsequently down-cast to an appropriate type. Object Identity and Proxy Comparison in C++ Proxy handles support comparison using the following operators: operator== operator!= These operators permit you to compare proxies for equality and inequality. To test whether a proxy is null, use a comparison with the literal , for example:0Ice 3.4.2 Documentation 198 Copyright © 2011, ZeroC, Inc. C++ if (proxy == 0) // It's a nil proxy else // It's a non?nil proxy operator< operator<= operator> operator>= Proxies support comparison. This allows you to place proxies into STL containers such as maps or sorted lists. Boolean comparison Proxies have a conversion operator to . The operator returns true if a proxy is not null, and false otherwise. This allows you tobool write: C++ BasePrx base = ...; if (base) // It's a non?nil proxy else // It's a nil proxy Note that proxy comparison uses of the information in a proxy for the comparison. This means that not only the object identity must matchall for a comparison to succeed, but other details inside the proxy, such as the protocol and endpoint information, must be the same. In other words, comparison with and tests for identity, object identity. A common mistake is to write code along the following lines:== != proxy not C++ Ice::ObjectPrx p1 = ...; // Get a proxy... Ice::ObjectPrx p2 = ...; // Get another proxy... if (p1 != p2) { // p1 and p2 denote different objects // WRONG! } else { // p1 and p2 denote the same object // Correct } Even though and differ, they may denote the same Ice object. This can happen because, for example, both and embed thep1 p2 p1 p2 same object identity, but each use a different protocol to contact the target object. Similarly, the protocols may be the same, but denote different endpoints (because a single Ice object can be contacted via several different transport endpoints). In other words, if two proxies compare equal with , we know that the two proxies denote the same object (because they are identical in all respects); however, if two== proxies compare unequal with , we know absolutely nothing: the proxies may or may not denote the same object.== To compare the object identities of two proxies, you can use helper functions in the namespace:Ice C++ namespace Ice { bool proxyIdentityLess(const ObjectPrx&, const ObjectPrx&); bool proxyIdentityEqual(const ObjectPrx&, const ObjectPrx&); bool proxyIdentityAndFacetLess(const ObjectPrx&, const ObjectPrx&); bool proxyIdentityAndFacetEqual(const ObjectPrx&, const ObjectPrx&); } The function returns true if the object identities embedded in two proxies are the same and ignores otherproxyIdentityEqual information in the proxies, such as facet and transport information. To include the in the comparison, use facet nameIce 3.4.2 Documentation 199 Copyright © 2011, ZeroC, Inc. instead.proxyIdentityAndFacetEqual The function establishes a total ordering on proxies. It is provided mainly so you can use object identity comparisonproxyIdentityLess with STL sorted containers. (The function uses as the major ordering criterion, and as the minor ordering criterion.) The name category function behaves similarly to , except that it also compares the facet names of theproxyIdentityAndFacetLess proxyIdentityLess proxies when their identities are equal. proxyIdentityEqual and allow you to correctly compare proxies for object identity. The exampleproxyIdentityAndFacetLess below demonstrates how to use :proxyIdentityEqual C++ Ice::ObjectPrx p1 = ...; // Get a proxy... Ice::ObjectPrx p2 = ...; // Get another proxy... if (!Ice::proxyIdentityEqual(p1, p2) { // p1 and p2 denote different objects // Correct } else { // p1 and p2 denote the same object // Correct } See Also Interfaces, Operations, and Exceptions Proxies C++ Mapping for Operations Example of a File System Client in C++ Using Proxies Facets and Versioning IceStormIce 3.4.2 Documentation 200 Copyright © 2011, ZeroC, Inc. C++ Mapping for Operations On this page: Basic C++ Mapping for Operations Normal and Operations in C++idempotent Passing Parameters in C++ In-Parameters in C++ Out-Parameters in C++ Chained Invocations in C++ Exception Handling in C++ Exceptions and Out-Parameters in C++ Exceptions and Return Values in C++ Basic C++ Mapping for Operations As we saw in the , for each on an interface, the proxy class contains a corresponding member functionC++ mapping for interfaces operation with the same name. To invoke an operation, you call it via the proxy handle. For example, here is part of the definitions for our :file system Slice module Filesystem { interface Node { idempotent string name(); }; // ... }; The proxy class for the interface, tidied up to remove irrelevant detail, is as follows:Node C++ namespace IceProxy { namespace Filesystem { class Node : virtual public IceProxy::Ice::Object { public: std::string name(); // ... }; typedef IceInternal::ProxyHandle NodePrx; // ... } // ... } The operation returns a value of type . Given a proxy to an object of type , the client can invoke the operation as follows:name string Node C++ NodePrx node = ...; // Initialize proxy string name = node?>name(); // Get name via RPC The proxy handle overloads to forward method calls to the underlying proxy class instance which, in turn, sends the operationoperator?> invocation to the server, waits until the operation is complete, and then unmarshals the return value and returns it to the caller. Because the return value is of type , it is safe to ignore the return value. For example, the following code contains no memory leak:stringIce 3.4.2 Documentation 201 Copyright © 2011, ZeroC, Inc. C++ NodePrx node = ...; // Initialize proxy node?>name(); // Useless, but no leak This is true for all mapped Slice types: you can safely ignore the return value of an operation, no matter what its type — return values are always returned by value. If you ignore the return value, no memory leak occurs because the destructor of the returned value takes care of deallocating memory as needed. Normal and Operations in C++idempotent You can add an qualifier to a Slice operation. As far as the signature for the corresponding proxy methods is concerned, idempotent has no effect. For example, consider the following interface:idempotent Slice interface Example { string op1(); idempotent string op2(); idempotent void op3(string s); }; The proxy class for this interface looks like this: C++ namespace IceProxy { class Example : virtual public IceProxy::Ice::Object { public: std::string op1(); std::string op2(); // idempotent void op3(const std::string&); // idempotent // ... }; } Because affects an aspect of call dispatch, not interface, it makes sense for the mapping to be unaffected by the idempotent idempotent keyword. Passing Parameters in C++ In-Parameters in C++ The parameter passing rules for the C++ mapping are very simple: parameters are passed either by value (for small values) or by const reference (for values that are larger than a machine word). Semantically, the two ways of passing parameters are identical: it is guaranteed that the value of a parameter will not be changed by the invocation (with some caveats — see below and Out-Parameters Location ).Transparency Here is an interface with operations that pass parameters of various types from client to server:Ice 3.4.2 Documentation 202 Copyright © 2011, ZeroC, Inc. Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ClientToServer { void op1(int i, float f, bool b, string s); void op2(NumberAndString ns, StringSeq ss, StringTable st); void op3(ClientToServer* proxy); }; The Slice compiler generates the following code for this definition: C++ struct NumberAndString { Ice::Int x; std::string str; // ... }; typedef std::vector StringSeq; typedef std::map StringTable; namespace IceProxy { class ClientToServer : virtual public IceProxy::Ice::Object { public: void op1(Ice::Int, Ice::Float, bool, const std::string&); void op2(const NumberAndString&, const StringSeq&, const StringTable&); void op3(const ClientToServerPrx&); // ... }; } Given a proxy to a interface, the client code can pass parameters as in the following example:ClientToServerIce 3.4.2 Documentation 203 Copyright © 2011, ZeroC, Inc. C++ ClientToServerPrx p = ...; // Get proxy... p?>op1(42, 3.14, true, "Hello world!"); // Pass simple literals int i = 42; float f = 3.14; bool b = true; string s = "Hello world!"; p?>op1(i, f, b, s); // Pass simple variables NumberAndString ns = { 42, "The Answer" }; StringSeq ss; ss.push_back("Hello world!"); StringTable st; st[0] = ss; p?>op2(ns, ss, st); // Pass complex variables p?>op3(p); // Pass proxy You can pass either literals or variables to the various operations. Because everything is passed by value or reference, there are noconst memory-management issues to consider. Out-Parameters in C++ The C++ mapping passes out-parameters by reference. Here is the once more, modified to pass all parameters in the Slice definition out direction: Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ServerToClient { void op1(out int i, out float f, out bool b, out string s); void op2(out NumberAndString ns, out StringSeq ss, out StringTable st); void op3(out ServerToClient* proxy); }; The Slice compiler generates the following code for this definition: C++ namespace IceProxy { class ServerToClient : virtual public IceProxy::Ice::Object { public: void op1(Ice::Int&, Ice::Float&, bool&, std::string&); void op2(NumberAndString&, StringSeq&, StringTable&); void op3(ServerToClientPrx&); // ... }; } Given a proxy to a interface, the client code can pass parameters as in the following example:ServerToClientIce 3.4.2 Documentation 204 Copyright © 2011, ZeroC, Inc. C++ ServerToClientPrx p = ...; // Get proxy... int i; float f; bool b; string s; p?>op1(i, f, b, s); // i, f, b, and s contain updated values now NumberAndString ns; StringSeq ss; StringTable st; p?>op2(ns, ss, st); // ns, ss, and st contain updated values now p?>op3(p); // p has changed now! Again, there are no surprises in this code: the caller simply passes variables to an operation; once the operation completes, the values of those variables will be set by the server. It is worth having another look at the final call: C++ p?>op3(p); // Weird, but well?defined Here, is the proxy that is used to dispatch the call. That same variable is also passed as an out-parameter to the call, meaning that thep p server will set its value. In general, passing the same parameter as both an input and output parameter is safe: the Ice run time will correctly handle all locking and memory-management activities. Another, somewhat pathological example is the following: Slice sequence Row; sequence Matrix; interface MatrixArithmetic { void multiply(Matrix m1, Matrix m2, out Matrix result); }; Given a proxy to a interface, the client code could do the following:MatrixArithmetic C++ MatrixArithmeticPrx ma = ...; // Get proxy... Matrix m1 = ...; // Initialize one matrix Matrix m2 = ...; // Initialize second matrix ma?>squareAndCubeRoot(m1, m2, m1); // !!! This code is technically legal, in the sense that no memory corruption or locking issues will arise, but it has surprising behavior: because the same variable is passed as an input parameter as well as an output parameter, the final value of is indeterminate — in particular, ifm1 m1 client and server are collocated in the same address space, the implementation of the operation will overwrite parts of the input matrix inm1 the process of computing the result because the result is written to the same physical memory location as one of the inputs. In general, you should take care when passing the same variable as both an input and output parameter and only do so if the called operation guarantees to be well-behaved in this case.Ice 3.4.2 Documentation 205 Copyright © 2011, ZeroC, Inc. Chained Invocations in C++ Consider the following simple interface containing two operations, one to set a value and one to get it: Slice interface Name { string getName(); void setName(string name); }; Suppose we have two proxies to interfaces of type , and , and chain invocations as follows:Name p1 p2 C++ p2?>setName(p1?>getName()); This works exactly as intended: the value returned by is transferred to . There are no memory-management or exception safety issuesp1 p2 with this code. Exception Handling in C++ Any operation invocation may throw and, if the operation has an exception specification, may also throw a run-time exception user . Suppose we have the following simple interface:exceptions Slice exception Tantrum { string reason; }; interface Child { void askToCleanUp() throws Tantrum; }; Slice exceptions are thrown as C++ exceptions, so you can simply enclose one or more operation invocations in a block:try-catch C++ ChildPrx child = ...; // Get proxy... try { child?>askToCleanUp(); // Give it a try... } catch (const Tantrum& t) { cout << "The child says: " << t.reason << endl; } Typically, you will catch only a few exceptions of specific interest around an operation invocation; other exceptions, such as unexpected run-time errors, will typically be dealt with by exception handlers higher in the hierarchy. For example:Ice 3.4.2 Documentation 206 Copyright © 2011, ZeroC, Inc. 1. C++ void run() { ChildPrx child = ...; // Get proxy... try { child?>askToCleanUp(); // Give it a try... } catch (const Tantrum& t) { cout << "The child says: " << t.reason << endl; child?>scold(); // Recover from error... } child?>praise(); // Give positive feedback... } int main(int argc, char* argv[]) { int status = 1; try { // ... run(); // ... status = 0; } catch (const Ice::Exception& e) { cerr << "Unexpected run?time error: " << e << endl; } // ... return status; } This code handles a specific exception of local interest at the point of call and deals with other exceptions generically. (This is also the strategy we used for our .)first simple application For efficiency reasons, you should always catch exceptions by reference. This permits the compiler to avoid calling the exception'sconst copy constructor (and, of course, prevents the exception from being sliced to a base type). Exceptions and Out-Parameters in C++ The Ice run time makes no guarantees about the state of out-parameters when an operation throws an exception: the parameter may have still have its original value or may have been changed by the operation's implementation in the target object. In other words, for out-parameters, Ice provides the weak exception guarantee but does not provide the strong exception guarantee.[1] This is done for reasons of efficiency: providing the strong exception guarantee would require more overhead than can be justified. Exceptions and Return Values in C++ For return values, C++ provides the guarantee that a variable receiving the return value of an operation will not be overwritten if an exception is thrown. (Of course, this guarantee holds only if you do not use the same variable as both an out-parameter and to receive the return value ).of an invocation See Also Operations Slice for a Simple File System C++ Mapping for Interfaces References Sutter, H. 1999. . Reading, MA: Addison-Wesley.Exceptional C++: 47 Engineering Puzzles, Programming Problems, and SolutionsIce 3.4.2 Documentation 207 Copyright © 2011, ZeroC, Inc.Ice 3.4.2 Documentation 208 Copyright © 2011, ZeroC, Inc. 1. C++ Mapping for Classes On this page: Basic C++ Mapping for Classes Inheritance from in C++Ice::Object Class Data Members in C++ Class Constructors in C++ Class Operations in C++ Class Factories in C++ Basic C++ Mapping for Classes A Slice is mapped to a C++ class with the same name. The generated class contains a public data member for each Slice dataclass member (just as for and ), and a virtual member function for each operation. Consider the following class definition:structures exceptions Slice class TimeOfDay { short hour; // 0 ? 23 short minute; // 0 ? 59 short second; // 0 ? 59 string format(); // Return time as hh:mm:ss }; The Slice compiler generates the following code for this definition: C++ class TimeOfDay; typedef IceInternal::ProxyHandle TimeOfDayPrx; typedef IceInternal::Handle TimeOfDayPtr; class TimeOfDay : virtual public Ice::Object { public: Ice::Short hour; Ice::Short minute; Ice::Short second; virtual std::string format() = 0; TimeOfDay() {}; TimeOfDay(Ice::Short, Ice::Short, Ice::Short); virtual bool ice_isA(const std::string&); virtual const std::string& ice_id(); static const std::string& ice_staticId(); typedef TimeOfDayPrx ProxyType; typedef TimeOfDayPtr PointerType; // ... }; The definitions are for template programming.ProxyType and PointerType There are a number of things to note about the generated code: The generated class inherits from . This means that all classes implicitly inherit from ,TimeOfDay Ice::Object Ice::ObjectIce 3.4.2 Documentation 209 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. which is the ultimate ancestor of all classes. Note that is the same as . In otherIce::Object not IceProxy::Ice::Object words, you pass a class where a proxy is expected and vice versa.cannot The generated class contains a public member for each Slice data member. The generated class has a constructor that takes one argument for each data member, as well as a default constructor. The generated class contains a pure virtual member function for each Slice operation. The generated class contains additional member functions: , , , and .ice_isA ice_id ice_staticId ice_factory The compiler generates a type definition . This type implements a smart pointer that wraps dynamically-allocatedTimeOfDayPtr instances of the class. In general, the name of this type is . Do not confuse this with — thatPtr Prx type exists as well, but is the proxy handle for the class, not a smart pointer. There is quite a bit to discuss here, so we will look at each item in turn. Inheritance from in C++Ice::Object Like interfaces, classes implicitly inherit from a common base class, . However, as shown in the figure below, classesIce::Object inherited from instead of (which is at the base of the inheritance hierarchy for proxies). As a result, youIce::Object Ice::ObjectPrx cannot pass a class where a proxy is expected (and vice versa) because the base types for classes and proxies are not compatible. Inheritance from and .Ice::ObjectPrx Ice::Object Ice::Object contains a number of member functions: C++ namespace Ice { class Object : public virtual IceInternal::GCShared { public: virtual bool ice_isA(const std::string&, const Current& = Current()) const; virtual void ice_ping(const Current& = Current()) const; virtual std::vector ice_ids(const Current& = Current()) const; virtual const std::string& ice_id(const Current& = Current()) const; static const std::string& ice_staticId(); virtual Ice::Int ice_getHash() const; virtual ObjectPtr ice_clone() const; virtual void ice_preMarshal(); virtual void ice_postUnmarshal(); virtual DispatchStatus ice_dispatch( Ice::Request&, const DispatchInterceptorAsyncCallbackPtr& = 0); virtual bool operator==(const Object&) const; virtual bool operator!=(const Object&) const; virtual bool operator<(const Object&) const; virtual bool operator<=(const Object&) const; virtual bool operator>(const Object&) const; virtual bool operator>=(const Object&) const; }; }Ice 3.4.2 Documentation 210 Copyright © 2011, ZeroC, Inc. The member functions of behave as follows:Ice::Object ice_isA This function returns if the object supports the given , and otherwise.true type ID false ice_ping As for interfaces, provides a basic reachability test for the class. Note that is normally only invoked on theice_ping ice_ping proxy for a class that might be remote because a class instance that is local (in the caller's address space) can always be reached. ice_ids This function returns a string sequence representing all of the supported by this object, including .type IDs ::Ice::Object ice_id This function returns the actual run-time for a class. If you call through a smart pointer to a base instance, thetype ID ice_id returned type id is the actual (possibly more derived) type ID of the instance. ice_staticId This function returns the static type ID of a class. ice_getHash This method returns a hash value for the class, allowing you to easily place classes into hash tables. ice_clone This function makes a .polymorphic shallow copy of a class ice_preMarshal The Ice run time invokes this function prior to marshaling the object's state, providing the opportunity for a subclass to validate its declared data members. ice_postUnmarshal The Ice run time invokes this function after unmarshaling an object's state. A subclass typically overrides this function when it needs to perform additional initialization using the values of its declared data members. ice_dispatch This function dispatches an incoming request to a servant. It is used in the implementation of .dispatch interceptors operator== operator!= operator< operator<= operator> operator>= The comparison operators permit you to use classes as elements of STL sorted containers. Note that sort order, unlike for structures , is based on the memory address of the class, not on the contents of its data members of the class. Class Data Members in C++ By default, data members of classes are mapped exactly as for structures and exceptions: for each data member in the Slice definition, the generated class contains a corresponding public data member. If you wish to restrict access to a data member, you can modify its visibility using the metadata directive. The presence of thisprotected directive causes the Slice compiler to generate the data member with protected visibility. As a result, the member can be accessed only by the class itself or by one of its subclasses. For example, the class shown below has the metadata directive appliedTimeOfDay protected to each of its data members: Slice class TimeOfDay { ["protected"] short hour; // 0 ? 23 ["protected"] short minute; // 0 ? 59 ["protected"] short second; // 0 ? 59 string format(); // Return time as hh:mm:ss }; The Slice compiler produces the following generated code for this definition:Ice 3.4.2 Documentation 211 Copyright © 2011, ZeroC, Inc. C++ class TimeOfDay : virtual public Ice::Object { public: virtual std::string format() = 0; // ... protected: Ice::Short hour; Ice::Short minute; Ice::Short second; }; For a class in which all of the data members are protected, the metadata directive can be applied to the class itself rather than to each member individually. For example, we can rewrite the class as follows:TimeOfDay Slice ["protected"] class TimeOfDay { short hour; // 0 ? 23 short minute; // 0 ? 59 short second; // 0 ? 59 string format(); // Return time as hh:mm:ss }; Class Constructors in C++ Classes have a default constructor that default-constructs each data member. Members having a complex type, such as strings, sequences, and dictionaries, are initialized by their own default constructor. However, the default constructor performs no initialization for members having one of the simple built-in types boolean, integer, floating point, or enumeration. For such a member, it is not safe to assume that the member has a reasonable default value. This is especially true for enumerated types as the member's default value may be outside the legal range for the enumeration, in which case an exception will occur during marshaling unless the member is explicitly set to a legal value. To ensure that data members of primitive types are initialized to reasonable values, you can declare default values in your .Slice definition The default constructor initializes each of these data members to its declared value. Classes also have a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assign to its members). For derived classes, the constructor has one parameter for each of the base class's data members, plus one parameter for each of the derived class's data members, in base-to-derived order. For example: Slice class Base { int i; }; class Derived extends Base { string s; }; This generates:Ice 3.4.2 Documentation 212 Copyright © 2011, ZeroC, Inc. C++ class Base : virtual public ::Ice::Object { public: ::Ice::Int i; Base() {}; explicit Base(::Ice::Int); // ... }; class Derived : public Base { public: ::std::string s; Derived() {}; Derived(::Ice::Int, const ::std::string&); // ... }; Note that single-parameter constructors are defined as , to prevent implicit argument conversions.explicit By default, derived classes derive non-virtually from their base class. If you need virtual inheritance, you can enable it using the metadata directive.["cpp:virtual"] Class Operations in C++ Operations of classes are mapped to pure virtual member functions in the generated class. This means that, if a class contains operations (such as the operation of our class), you must provide an implementation of the operation in a class that is derived fromformat TimeOfDay the generated class. For example: C++ class TimeOfDayI : virtual public TimeOfDay { public: virtual std::string format() { std::ostringstream s; s << setw(2) << setfill('0') << hour << ":"; s << setw(2) << setfill('0') << minute << ":"; s << setw(2) << setfill('0') << second; return s.c_str(); } protected: virtual ~TimeOfDayI() {} // Optional }; We discuss the motivation for the protected destructor in .Preventing Stack-Allocation of Class Instances Class Factories in C++ Having created a class such as , we have an implementation and we can instantiate the class, but we cannotTimeOfDayI TimeOfDayI receive it as the return value or as an out-parameter from an operation invocation. To see why, consider the following simple interface:Ice 3.4.2 Documentation 213 Copyright © 2011, ZeroC, Inc. Slice interface Time { TimeOfDay get(); }; When a client invokes the operation, the Ice run time must instantiate and return an instance of the class. However, get TimeOfDay is an abstract class that cannot be instantiated. Unless we tell it, the Ice run time cannot magically know that we have created a TimeOfDay class that implements the abstract operation of the abstract class. In other words, we must provide theTimeOfDayI format TimeOfDay Ice run time with a factory that knows that the abstract class has a concrete implementation. The TimeOfDay TimeOfDayI interface provides us with the necessary operations:Ice::Communicator Slice module Ice { local interface ObjectFactory { Object create(string type); void destroy(); }; local interface Communicator { void addObjectFactory(ObjectFactory factory, string id); ObjectFactory findObjectFactory(string id); // ... }; }; To supply the Ice run time with a factory for our class, we must implement the interface:TimeOfDayI ObjectFactory Slice module Ice { local interface ObjectFactory { Object create(string type); void destroy(); }; }; The object factory's operation is called by the Ice run time when it needs to instantiate a class. The factory's create TimeOfDay destroy operation is called by the Ice run time when its communicator is destroyed. A possible implementation of our object factory is: C++ class ObjectFactory : public Ice::ObjectFactory { public: virtual Ice::ObjectPtr create(const std::string& type) { assert(type == M::TimeOfDay::ice_staticId()); return new TimeOfDayI; } virtual void destroy() {} }; The method is passed the of the class to instantiate. For our class, the type ID is . Ourcreate type ID TimeOfDay "::M::TimeOfDay" implementation of checks the type ID: if it matches, the method instantiates and returns a object. For other type IDs,create TimeOfDayI the method asserts because it does not know how to instantiate other types of objects. Note that we used the method to obtain the type ID rather than embedding a literal string. Using a literal type ID string inice_staticId your code is discouraged because it can lead to errors that are only detected at run time. For example, if a Slice class or one of its enclosing modules is renamed and the literal string is not changed accordingly, a receiver will fail to unmarshal the object and the Ice run time will raise . By using instead, we avoid any risk of a misspelled or obsolete type ID, and we canNoObjectFactoryException ice_staticId discover at compile time if a Slice class or module has been renamed.Ice 3.4.2 Documentation 214 Copyright © 2011, ZeroC, Inc. Given a factory implementation, such as our , we must inform the Ice run time of the existence of the factory:ObjectFactory C++ Ice::CommunicatorPtr ic = ...; ic?>addObjectFactory(new ObjectFactory, M::TimeOfDay::ice_staticId()); Now, whenever the Ice run time needs to instantiate a class with the type ID , it calls the method of the"::M::TimeOfDay" create registered instance.ObjectFactory The operation of the object factory is invoked by the Ice run time when the communicator is destroyed. This gives you a chance todestroy clean up any resources that may be used by your factory. Do not call on the factory while it is registered with the communicator —destroy if you do, the Ice run time has no idea that this has happened and, depending on what your implementation is doing, may causedestroy undefined behavior when the Ice run time tries to next use the factory. The run time guarantees that will be the last call made on the factory, that is, will not be called concurrently with destroy create destroy , and will not be called once has been called. However, calls to can be made concurrently.create destroy create Note that you cannot register a factory for the same type ID twice: if you call with a type ID for which a factory isaddObjectFactory registered, the Ice run time throws an .AlreadyRegisteredException Finally, keep in mind that if a class has only data members, but no operations, you need not create and register an object factory to transmit instances of such a class. Only if a class has operations do you have to define and register an object factory. See Also Classes Smart Pointers for Classes C++ Mapping for Operations Asynchronous Method Invocation (AMI) in C++ Dispatch InterceptorsIce 3.4.2 Documentation 215 Copyright © 2011, ZeroC, Inc. Smart Pointers for Classes On this page: Automatic Memory Management with Smart Pointers Copying and Assignment of Classes Polymorphic Copying of Classes Null Smart Pointers Preventing Stack-Allocation of Class Instances Smart Pointers and Constructors Smart Pointers and Exception Safety Smart Pointers and Cycles Garbage Collection of Class Instances Smart Pointer Comparison Automatic Memory Management with Smart Pointers A recurring theme for C++ programmers is the need to deal with memory allocations and deallocations in their programs. The difficulty of doing so is well known: in the face of exceptions, multiple return paths from functions, and callee-allocated memory that must be deallocated by the caller, it can be extremely difficult to ensure that a program does not leak resources. This is particularly important in multi-threaded programs: if you do not rigorously track ownership of dynamic memory, a thread may delete memory that is still used by another thread, usually with disastrous consequences. To alleviate this problem, Ice provides smart pointers for classes. These smart pointers use reference counting to keep track of each class instance and, when the last reference to a class instance disappears, automatically delete the instance. Smart pointer classes are an example of the ( ) idiom .RAII Resource Acquisition Is Initialization [1] Smart pointers are generated by the Slice compiler for each class type. For a Slice class , the compiler generates a C++ smart pointer called . Rather than showing all the details of the generated class, here is the basic usage pattern:Ptr whenever you allocate a class instance on the heap, you simply assign the pointer returned from to a smart pointer for the class.new Thereafter, memory management is automatic and the class instance is deleted once the last smart pointer for it goes out of scope: C++ { // Open scope TimeOfDayPtr tod = new TimeOfDayI; // Allocate instance // Initialize... tod->hour = 18; tod->minute = 11; tod->second = 15; // ... } // No memory leak here! As you can see, you use to access the members of the class via its smart pointer. When the smart pointer goes out ofoperator-> tod scope, its destructor runs and, in turn, the destructor takes care of calling on the underlying class instance, so no memory is leaked.delete A smart pointer performs reference counting of its underlying class instance: The constructor of a class sets its reference count to zero. Initializing a smart pointer with a dynamically-allocated class instance causes the smart pointer to increment the reference count of the instance by one. Copy-constructing a smart pointer increments the reference count of the instance by one. Assigning one smart pointer to another increments the target's reference count and decrements the source's reference count. (Self-assignment is safe.) The destructor of a smart pointer decrements the reference count by one and calls on its class instance if the referencedelete count drops to zero. Suppose that we default-construct a smart pointer as follows:Ice 3.4.2 Documentation 216 Copyright © 2011, ZeroC, Inc. C++ TimeOfDayPtr tod; This creates a smart pointer with an internal null pointer. Newly initialized smart pointer. Constructing a class instance creates that instance with a reference count of zero; the assignment to the smart pointer causes the smart pointer to increment the instance's reference count: C++ tod = new TimeOfDayI; // Refcount == 1 The resulting situation is shown below: Initialized smart pointer. Assigning or copy-constructing a smart pointer assigns and copy-constructs the smart pointer (not the underlying instance) and increments the reference count of the instance: C++ TimeOfDayPtr tod2(tod); // Copy-construct tod2 TimeOfDayPtr tod3; tod3 = tod; // Assign to tod3 Here is the situation after executing these statements: Three smart pointers pointing at the same class instance. Continuing the example, we can construct a second class instance and assign it to one of the original smart pointers, :tod2 C++ tod2 = new TimeOfDayI; This decrements the reference count of the instance originally denoted by and increments the reference count of the instance that istod2 assigned to . The resulting situation becomes the following:tod2Ice 3.4.2 Documentation 217 Copyright © 2011, ZeroC, Inc. Three smart pointers and two instances. You can clear a smart pointer by assigning zero to it: C++ tod = 0; // Clear handle As you would expect, this decrements the reference count of the instance, as shown here: Decremented reference count after clearing a smart pointer. If a smart pointer goes out of scope, is cleared, or has a new instance assigned to it, the smart pointer decrements the reference count of its instance; if the reference count drops to zero, the smart pointer calls to deallocate the instance. The following code snippetdelete deallocates the instance on the right by assigning to :tod2 tod3 C++ tod3 = tod2; This results in the following situation: Deallocation of an instance with a reference count of zero. Copying and Assignment of Classes Classes have a default memberwise copy constructor and assignment operator, so you can copy and assign class instances:Ice 3.4.2 Documentation 218 Copyright © 2011, ZeroC, Inc. C++ TimeOfDayPtr tod = new TimeOfDayI(2, 3, 4); // Create instance TimeOfDayPtr tod2 = new TimeOfDayI(*tod); // Copy instance TimeOfDayPtr tod3 = new TimeOfDayI; *tod3 = *tod; // Assign instance Copying and assignment in this manner performs a memberwise shallow copy or assignment, that is, the source members are copied into the target members; if a class contains class members (which are mapped as smart pointers), what is copied or assigned is the smart pointer, not the target of the smart pointer. To illustrate this, consider the following Slice definitions: Slice class Node { int i; Node next; }; Assume that we initialize two instances of type as follows:Node C++ NodePtr p1 = new Node(99, new Node(48, 0)); NodePtr p2 = new Node(23, 0); // ... *p2 = *p1; // Assignment After executing the first two statements, we have the situation shown below: Class instances prior to assignment. After executing the assignment statement, we end up with this result:Ice 3.4.2 Documentation 219 Copyright © 2011, ZeroC, Inc. Class instances after assignment. Note that copying and assignment also works for the implementation of abstract classes, such as our class, for example:TimeOfDayI C++ class TimeOfDayI; typedef IceUtil::Handle TimeOfDayIPtr; class TimeOfDayI : virtual public TimeOfDay { // As before... }; The default copy constructor and assignment operator will perform a memberwise copy or assignment of your implementation class: C++ TimeOfDayIPtr tod1 = new TimeOfDayI; TimeOfDayIPtr tod2 = new TimeOfDayI(*tod1); // Make copy Of course, if your implementation class contains raw pointers (for which a memberwise copy would almost certainly be inappropriate), you must implement your own copy constructor and assignment operator that take the appropriate action (and probably call the base copy constructor or assignment operator to take care of the base part). Note that the preceding code uses as a typedef for . This class is a template thatTimeOfDayIPtr IceUtil::Handle contains the smart pointer implementation. If you want smart pointers for the implementation of an abstract class, you must define a smart pointer type as illustrated by this type definition. Copying and assignment of classes also works correctly for derived classes: you can assign a derived class to a base class, but not vice-versa; during such an assignment, the derived part of the source class is sliced, as per the usual C++ assignment semantics. Polymorphic Copying of Classes As shown in , the C++ mapping generates an member function for every class:Inheritance from Ice::Object ice_clone C++ class TimeOfDay : virtual public Ice::Object { public: // ... virtual Ice::ObjectPtr ice_clone() const; };Ice 3.4.2 Documentation 220 Copyright © 2011, ZeroC, Inc. This member function makes a polymorphic shallow copy of a class: members that are not class members are deep copied; all members that are class members are shallow copied. To illustrate, consider the following class definition: Slice class Node { Node n1; Node n2; }; Assume that we have an instance of this class, with the and members initialized to point at separate instances, as shown below:n1 n2 A class instance pointing at two other instances. If we call on the instance on the left, we end up with this situation:ice_clone Resulting graph after calling ice_clone on the left-most instance. As you can see, class members are shallow copied, that is, makes a copy of the class instance on which it is invoked, but doesice_clone not copy any class instances that are pointed at by the copied instance. Note that returns a value of type , to avoid problems with compilers that do not support covariant returnice_clone Ice::ObjectPtr types. The generated classes contain a member that allows you to safely down-cast the return value of . ForPtr dynamicCast ice_clone example, the code to achieve the situation shown in the illustration above, looks as follows: C++ NodePtr p1 = new Node(new Node, new Node); NodePtr p2 = NodePtr::dynamicCast(p1->ice_clone()); ice_clone is generated by the Slice compiler for concrete classes (that is, classes that do not have operations). However, because classes with operations are abstract, the generated for abstract classes cannot know how to instantiate an instance of the derivedice_clone concrete class (because the name of the derived class is not known). This means that, for abstract classes, the generated ice_cloneIce 3.4.2 Documentation 221 Copyright © 2011, ZeroC, Inc. throws a .CloneNotImplementedException If you want to clone the implementation of an abstract class, you must override the virtual member in your concreteice_clone implementation class. For example: C++ class TimeOfDayI : public TimeOfDay { public: virtual Ice::ObjectPtr ice_clone() const { return new TimeOfDayI(*this); } }; Null Smart Pointers A null smart pointer contains a null C++ pointer to its underlying instance. This means that if you attempt to dereference a null smart pointer, you get an :IceUtil::NullHandleException C++ TimeOfDayPtr tod; // Construct null handle try { tod->minute = 0; // Dereference null handle assert(false); // Cannot get here } catch (const IceUtil::NullHandleException&) { ; // OK, expected } catch (...) { assert(false); // Must get NullHandleException } Preventing Stack-Allocation of Class Instances The Ice C++ mapping expects class instances to be allocated on the heap. Allocating class instances on the stack or in static variables is pragmatically useless because all the Ice APIs expect parameters that are smart pointers, not class instances. This means that, to do anything with a stack-allocated class instance, you must initialize a smart pointer for the instance. However, doing so does not work because it inevitably leads to a crash: C++ { // Enter scope TimeOfDayI t; // Stack-allocated class instance TimeOfDayPtr todp; // Handle for a TimeOfDay instance todp = &t; // Legal, but dangerous // ... } // Leave scope, looming crash! This goes badly wrong because, when goes out of scope, it decrements the reference count of the class to zero, which then calls todp on itself. However, the instance is stack-allocated and cannot be deleted, and we end up with undefined behavior (typically, a coredelete dump). The following attempt to fix this is also doomed to failure:Ice 3.4.2 Documentation 222 Copyright © 2011, ZeroC, Inc. C++ { // Enter scope TimeOfDayI t; // Stack-allocated class instance TimeOfDayPtr todp; // Handle for a TimeOfDay instance todp = &t; // Legal, but dangerous // ... todp = 0; // Crash imminent! } This code attempts to circumvent the problem by clearing the smart pointer explicitly. However, doing so also causes the smart pointer to drop the reference count on the class to zero, so this code ends up with the same call to on the stack-allocated instance as thedelete previous example. The upshot of all this is: . The C++ mapping assumes that all classnever allocate a class instance on the stack or in a static variable instances are allocated on the heap and no amount of coding trickery will change this. You could abuse the member to disable deallocation, but we strongly discourage you from doing this.__setNoDelete You can prevent allocation of class instances on the stack or in static variables by adding a protected destructor to your implementation of the class: if a class has a protected destructor, allocation must be made with , and static or stack allocation causes a compile-time error.new In addition, explicit calls to on a heap-allocated instance also are flagged at compile time.delete Tip You may want to habitually add a protected destructor to your implementation of abstract Slice classes to protect yourself from accidental heap allocation, as shown in . (For Slice classes that do not have operations, the SliceClass Operations compiler automatically adds a protected destructor.) Smart Pointers and Constructors Slice classes inherit their reference-counting behavior from the class, which ensures that reference counts are managedIceUtil::Shared in a thread-safe manner. When a stack-allocated smart pointer goes out of scope, the smart pointer invokes the function on the__decRef reference-counted object. Ignoring thread-safety issues, is implemented like this:__decRef C++ void IceUtil::Shared::__decRef() { if (--_ref == 0 && !_noDelete) delete this; } In other words, when the smart pointer calls on the pointed-at instance and the reference count reaches zero (which happens__decRef when the last smart pointer for a class instance goes out of scope), the instance self-destructs by calling .delete this However, as you can see, the instance self-destructs only if is false (which it is by default, because the constructor initializes it_noDelete to false). You can call to prevent this self-destruction and, later, call to enable it again.__setNoDelete(true) __setNoDelete(false) This is necessary if, for example, a class in its constructor needs to pass to another function:thisIce 3.4.2 Documentation 223 Copyright © 2011, ZeroC, Inc. C++ void someFunction(const TimeOfDayPtr& t) { // ... } TimeOfDayI::TimeOfDayI() { someFunction(this); // Trouble looming here! } At first glance, this code looks innocuous enough. While is being constructed, it passes to , whichTimeOfDayI this someFunction expects a smart pointer. The compiler constructs a temporary smart pointer at the point of call (because the smart pointer template has a single-argument constructor that accepts a pointer to a heap-allocated instance, so the constructor acts as a conversion function). However, this code fails badly. The instance is constructed with a statement such as:TimeOfDayI C++ TimeOfDayPtr tod = new TimeOfDayI; The constructor of is called by and, when the constructor starts executing, the reference count of the instanceTimeOfDayI operator new is zero (because that is what the reference count is initialized to by the base class of ). When the constructor calls Shared TimeOfDayI , the compiler creates a temporary smart pointer, which increments the reference count of the instance and, once someFunction completes, the compiler dutifully destroys that temporary smart pointer again. But, of course, that drops the reference countsomeFunction back to zero and causes the instance to self-destruct by calling . The net effect is that the call to TimeOfDayI delete this new returns a pointer to an already deleted object, which is likely to cause the program to crash.TimeOfDayI To get around the problem, you can call :__setNoDelete C++ TimeOfDayI::TimeOfDayI() { __setNoDelete(true); someFunction(this); __setNoDelete(false); } The code disables self-destruction while uses its temporary smart pointer by calling . By doingsomeFunction __setNoDelete(true) this, the reference count of the instance is incremented before is called and decremented back to zero when someFunction completes without causing the object to self-destruct. The constructor then re-enables self-destruction by calling someFunction before returning, so the statement__setNoDelete(false) C++ TimeOfDayPtr tod = new TimeOfDayI; does the usual thing, namely to increment the reference count of the object to 1, despite the fact that a temporary smart pointer existed while the constructor ran. In general, whenever a class constructor passes to a function or another class that accepts a smart pointer, youthis must temporarily disable self-destruction. Smart Pointers and Exception Safety Smart pointers are exception safe: if an exception causes the thread of execution to leave a scope containing a stack-allocated smart pointer, the C++ run time ensures that the smart pointer's destructor is called, so no resource leaks can occur:Ice 3.4.2 Documentation 224 Copyright © 2011, ZeroC, Inc. C++ { // Enter scope... TimeOfDayPtr tod = new TimeOfDayI; // Allocate instance someFuncThatMightThrow(); // Might throw... // ... } // No leak here, even if an exception is thrown If an exception is thrown, the destructor of runs and ensures that it deallocates the underlying class instance.tod There is one potential pitfall you must be aware of though: if a constructor of a base class throws an exception, and another class instance holds a smart pointer to the instance being constructed, you can end up with a double deallocation. You can use the __setNoDelete mechanism to temporarily disable self-destruction in this case, as described .above Smart Pointers and Cycles One thing you need to be aware of is the inability of reference counting to deal with cyclic dependencies. For example, consider the following simple self-referential class: Slice class Node { int val; Node next; }; Intuitively, this class implements a linked list of nodes. As long as there are no cycles in the list of nodes, everything is fine, and our smart pointers will correctly deallocate the class instances. However, if we introduce a cycle, we have a problem: C++ { // Open scope... NodePtr n1 = new Node; // N1 refcount == 1 NodePtr n2 = new Node; // N2 refcount == 1 n1->next = n2; // N2 refcount == 2 n2->next = n1; // N1 refcount == 2 } // Destructors run: // N2 refcount == 1, // N1 refcount == 1, memory leak! The nodes pointed to by and do not have names but, for the sake of illustration, let us assume that 's node is called N1, and 'sn1 n2 n1 n2 node is called N2. When we allocate the N1 instance and assign it to , the smart pointer increments N1's reference count to 1.n1 n1 Similarly, N2's reference count is 1 after allocating the node and assigning it to . The next two statements set up a cyclic dependencyn2 between and by making their pointers point at each other. This sets the reference count of both N1 and N2 to 2. When then1 n2 next enclosing scope closes, the destructor of is called first and decrements N2's reference count to 1, followed by the destructor of , whichn2 n1 decrements N1's reference count to 1. The net effect is that neither reference count ever drops to zero, so both N1 and N2 are leaked. Garbage Collection of Class Instances The previous example illustrates a problem that is generic to using reference counts for deallocation: if a cyclic dependency exists anywhere in a graph (possibly via many intermediate nodes), all nodes in the cycle are leaked. To avoid memory leaks due to such cycles, Ice for C++ contains a garbage collector. The collector identifies class instances that are part of one or more cycles but are no longer reachable from the program and deletes such instances: By default, garbage is collected whenever you destroy a communicator. This means that no memory is leaked when your programIce 3.4.2 Documentation 225 Copyright © 2011, ZeroC, Inc. exits. (Of course, this assumes that you correctly .)destroy your communicators You can also explicitly run the garbage collector by calling . For example, the leak caused by theIce::collectGarbage preceding example can be avoided as follows: C++ { // Open scope... NodePtr n1 = new Node; // N1 refcount == 1 NodePtr n2 = new Node; // N2 refcount == 1 n1->next = n2; // N1 refcount == 2 n2->next = n1; // N2 refcount == 2 } // Destructors run: // N2 refcount == 1, // N1 refcount == 1 Ice::collectGarbage(); // Deletes N1 and N2 The call to deletes the no longer reachable instances N1 and N2 (as well as any other non-reachableIce::collectGarbage instances that may have accumulated earlier). Deleting leaked memory with explicit calls to the garbage collector can be inconvenient because it requires polluting the code with calls to the collector. You can ask the Ice run time to run a garbage collection thread that periodically cleans up leaked memory by setting the property to a non-zero value. For example, setting to 5 causes the collectorIce.GC.Interval Ice.GC.Interval thread to run the garbage collector once every five seconds. You can trace the execution of the collector by setting Ice.Trace.GC to a non-zero value. Note that the garbage collector is useful only if your program actually creates cyclic class graphs. There is no point in running the garbage collector in programs that do not create such cycles. (For this reason, the collector thread is disabled by default and runs only if you explicitly set to a non-zero value.)Ice.GC.Interval Smart Pointer Comparison As for , class handles support the comparison operators , , and . This allows you to use class handles in STL sortedproxy handles == != < containers. Be aware that, for smart pointers, object identity is not used for the comparison, because class instances do not have identity. Instead, these operators simply compare the memory address of the classes they point to. This means that returns true only ifoperator== two smart pointers point at the same physical class instance: C++ // Create a class instance and initialize // TimeOfDayIPtr p1 = new TimeOfDayI; p1->hour = 23; p1->minute = 10; p1->second = 18; // Create another class instance with // the same member values // TimeOfDayIPtr p2 = new TimeOfDayI; p2->hour = 23; p2->minute = 10; p2->second = 18; assert(p1 != p2); // The two do not compare equal TimeOfDayIPtr p3 = p1; // Point at first class again assert(p1 == p3); // Now they compare equal See Also Classes C++ Mapping for ClassesIce 3.4.2 Documentation 226 Copyright © 2011, ZeroC, Inc. 1. Asynchronous Method Invocation (AMI) in C++ The Server-Side main Function in C++ Properties and Configuration The C++ Shared and SimpleShared Classes References Stroustrup, B. 1997. . Reading, MA: Addison-Wesley.The C++ Programming LanguageIce 3.4.2 Documentation 227 Copyright © 2011, ZeroC, Inc. Asynchronous Method Invocation (AMI) in C++ Asynchronous Method Invocation (AMI) is the term used to describe the client-side support for the asynchronous programming model. AMI supports both oneway and twoway requests, but unlike their synchronous counterparts, AMI requests never block the calling thread. When a client issues an AMI request, the Ice run time hands the message off to the local transport buffer or, if the buffer is currently full, queues the request for later delivery. The application can then continue its activities and poll or wait for completion of the invocation, or receive a callback when the invocation completes. AMI is transparent to the server: there is no way for the server to tell whether a client sent a request synchronously or asynchronously. As of version 3.4, Ice provides a new API for asynchronous method invocation. This page describes the new API. Note that the is deprecated and will be removed in a future release.old API On this page: Basic Asynchronous API in C++ Asynchronous Proxy Methods in C++ Asynchronous Exception Semantics in C++ Class in C++AsyncResult Polling for Completion in C++ Generic Completion Callbacks in C++ Using Cookies for Generic Completion Callbacks in C++ Type-Safe Completion Callbacks in C++ Using Cookies for Type-Safe Completion Callbacks in C++ Asynchronous Oneway Invocations in C++ Flow Control in C++ Asynchronous Batch Requests in C++ Concurrency Semantics for AMI in C++ AMI Limitations in C++ Basic Asynchronous API in C++ Consider the following simple Slice definition: Slice module Demo { interface Employees { string getName(int number); }; }; Asynchronous Proxy Methods in C++ Besides the synchronous proxy methods, generates the following asynchronous proxy methods:slice2cpp C++ Ice::AsyncResultPtr begin_getName(Ice::Int number); Ice::AsyncResultPtr begin_getName(Ice::Int number, const Ice::Context& __ctx) std::string end_getName(const Ice::AsyncResultPtr&); Four additional overloads of are generated for use with and .begin_getName generic callbacks type-safe callbacks As you can see, the single operation results in and methods. (The method is overloadedgetName begin_getName end_getName begin_ so you can pass a .)per-invocation context The method sends (or queues) an invocation of . This method does not block the calling thread.begin_getName getNameIce 3.4.2 Documentation 228 Copyright © 2011, ZeroC, Inc. The method collects the result of the asynchronous invocation. If, at the time the calling thread calls ,end_getName end_getName the result is not yet available, the calling thread blocks until the invocation completes. Otherwise, if the invocation completed some time before the call to , the method returns immediately with the result.end_getName A client could call these methods as follows: C++ EmployeesPrx e = ...; Ice::AsyncResultPtr r = e->begin_getName(99); // Continue to do other things here... string name = e->end_getName(r); Because does not block, the calling thread can do other things while the operation is in progress.begin_getName Note that returns a value of type . The associated with this smart pointer contains thebegin_getName AsyncResultPtr AsyncResult state that the Ice run time requires to keep track of the asynchronous invocation. You must pass the that is returned byAsyncResultPtr the method to the corresponding method.begin_ end_ The method has one parameter for each in-parameter of the corresponding Slice operation. Similarly, the method has onebegin_ end_ out-parameter for each out-parameter of the corresponding Slice operation (plus the parameter). For example, considerAsyncResultPtr the following operation: Slice double op(int inp1, string inp2, out bool outp1, out long outp2); The and methods have the following signature:begin_op end_op C++ Ice::AsyncResultPtr begin_op(Ice::Int inp1, const ::std::string& inp2) Ice::Double end_op(bool& outp1, Ice::Long& outp2, const Ice::AsyncResultPtr&); Asynchronous Exception Semantics in C++ If an invocation raises an exception, the exception is thrown by the method, even if the actual error condition for the exception wasend_ encountered during the method ("on the way out"). The advantage of this behavior is that all exception handling is located with thebegin_ code that calls the method (instead of being present twice, once where the method is called, and again where the end_ begin_ end_ method is called). There is one exception to the above rule: if you destroy the communicator and then make an asynchronous invocation, the methodbegin_ throws . This is necessary because, once the run time is finalized, it can no longer throw anCommunicatorDestroyedException exception from the method.end_ The only other exception that is thrown by the and methods is . This exceptionbegin_ end_ IceUtil::IllegalArgumentException indicates that you have used the API incorrectly. For example, the method throws this exception if you call an operation that has abegin_ return value or out-parameters on a oneway proxy. Similarly, the method throws this exception if you use a different proxy to call the end_ method than the proxy you used to call the method, or if the you pass to the method was obtained byend_ begin_ AsyncResult end_ calling the method for a different operation.begin_ AsyncResult Class in C++ The that is returned by the method encapsulates the state of the asynchronous invocation:AsyncResult begin_Ice 3.4.2 Documentation 229 Copyright © 2011, ZeroC, Inc. C++ class AsyncResult : virtual public IceUtil::Shared, private IceUtil::noncopyable { public: virtual bool operator==(const AsyncResult&) const; virtual bool operator<(const AsyncResult&) const; virtual Int getHash() const; virtual CommunicatorPtr getCommunicator() const; virtual ConnectionPtr getConnection() const; virtual ObjectPrx getProxy() const; const string& getOperation() const; LocalObjectPtr getCookie() const; bool isCompleted() const; void waitForCompleted(); bool isSent() const; void waitForSent(); void throwLocalException() const; bool sentSynchronously() const; }; The methods have the following semantics: bool operator==(const AsyncResult&) const bool operator<(const AsyncResult&) const Int getHash() const These methods allow you to create ordered or hashed collections of pending asynchronous invocations. This is useful, for example, if you can have a number of outstanding requests, and need to pass state between the and the methods. In this case,begin_ end_ you can use the returned {{AsyncResult}}s as keys into a map that stores the state for each call. CommunicatorPtr getCommunicator() const This method returns the communicator that sent the invocation. virtual ConnectionPtr getConnection() const This method returns the connection that was used for the invocation. virtual ObjectPrx getProxy() const This method returns the proxy that was used to call the method.begin_ const string& getOperation() const This method returns the name of the operation. LocalObjectPtr getCookie() const This method returns the that was passed to the method. If you did not pass a cookie to the method, thecookie begin_ begin_ return value is null. bool isCompleted() const This method returns true if, at the time it is called, the result of an invocation is available, indicating that a call to the methodend_ will not block the caller. Otherwise, if the result is not yet available, the method returns false. void waitForCompleted() This method blocks the caller until the result of an invocation becomes available. bool isSent() const When you call the method, the Ice run time attempts to write the corresponding request to the client-side transport. If thebegin_ transport cannot accept the request, the Ice run time queues the request for later transmission. returns true if, at the time itisSent is called, the request has been written to the local transport (whether it was initially queued or not). Otherwise, if the request is still queued or an exception occurred before the request could be sent, returns false.isSent void waitForSent() This method blocks the calling thread until a request has been written to the client-side transport, or an exception occurs. After returns, returns true if the request was successfully written to the client-side transport, or false if anwaitForSent isSentIce 3.4.2 Documentation 230 Copyright © 2011, ZeroC, Inc. exception occurred. In the case of a failure, you can call the corresponding method or to obtain theend_ throwLocalException exception. void throwLocalException() const This method throws the local exception that caused the invocation to fail. If no exception has occurred yet, throwLocalException does nothing. bool sentSynchronously() const This method returns true if a request was written to the client-side transport without first being queued. If the request was initially queued, returns false (independent of whether the request is still in the queue or has since been written tosentSynchronously the client-side transport). Polling for Completion in C++ The methods allow you to poll for call completion. Polling is useful in a variety of cases. As an example, consider theAsyncResult following simple interface to transfer files from client to server: Slice interface FileTransfer { void send(int offset, ByteSeq bytes); }; The client repeatedly calls to send a chunk of the file, indicating at which offset in the file the chunk belongs. A naïve way to transmit asend file would be along the following lines: C++ FileHandle file = open(...); FileTransferPrx ft = ...; const int chunkSize = ...; Ice::Int offset = 0; while (!file.eof()) { ByteSeq bs; bs = file.read(chunkSize); // Read a chunk ft->send(offset, bs); // Send the chunk offset += bs.size(); } This works, but not very well: because the client makes synchronous calls, it writes each chunk on the wire and then waits for the server to receive the data, process it, and return a reply before writing the next chunk. This means that both client and server spend much of their time doing nothing — the client does nothing while the server processes the data, and the server does nothing while it waits for the client to send the next chunk. Using asynchronous calls, we can improve on this considerably:Ice 3.4.2 Documentation 231 Copyright © 2011, ZeroC, Inc. C++ FileHandle file = open(...); FileTransferPrx ft = ...; const int chunkSize = ...; Ice::Int offset = 0; list results; const int numRequests = 5; while (!file.eof()) { ByteSeq bs; bs = file.read(chunkSize); // Send up to numRequests + 1 chunks asynchronously. Ice::AsyncResultPtr r = ft->begin_send(offset, bs); offset += bs.size(); // Wait until this request has been passed to the transport. r->waitForSent(); results.push_back(r); // Once there are more than numRequests, wait for the least // recent one to complete. while (results.size() > numRequests) { Ice::AsyncResultPtr r = results.front(); results.pop_front(); r->waitForCompleted(); } } // Wait for any remaining requests to complete. while (!results.empty()) { Ice::AsyncResultPtr r = results.front(); results.pop_front(); r->waitForCompleted(); } With this code, the client sends up to chunks before it waits for the least recent one of these requests to complete. InnumRequests + 1 other words, the client sends the next request without waiting for the preceding request to complete, up to the limit set by . InnumRequests effect, this allows the client to "keep the pipe to the server full of data": the client keeps sending data, so both client and server continuously do work. Obviously, the correct chunk size and value of depend on the bandwidth of the network as well as the amount of time takennumRequests by the server to process each request. However, with a little testing, you can quickly zoom in on the point where making the requests larger or queuing more requests no longer improves performance. With this technique, you can realize the full bandwidth of the link to within a percent or two of the theoretical bandwidth limit of a native socket connection. Generic Completion Callbacks in C++ The method is overloaded to allow you to provide completion callbacks. Here are the corresponding methods for the begin_ getName operation:Ice 3.4.2 Documentation 232 Copyright © 2011, ZeroC, Inc. C++ Ice::AsyncResultPtr begin_getName( Ice::Int number, const Ice::CallbackPtr& __del, const Ice::LocalObjectPtr& __cookie = 0); Ice::AsyncResultPtr begin_getName( Ice::Int number, const Ice::Context& __ctx, const Ice::CallbackPtr& __del, const Ice::LocalObjectPtr& __cookie = 0); The second version of lets you override the default context. (We discuss the purpose of the parameter in the nextbegin_getName cookie section.) Following the in-parameters, the method accepts a parameter of type . This is a smart pointer to abegin_ Ice::CallbackPtr callback class that is provided by the Ice run time. This class stores an instance of a callback class that you implement. The Ice run time invokes a method on your callback instance when an asynchronous operation completes. Your callback class must provide a method that returns and accepts a single parameter of type , for example:void const AsyncResultPtr& C++ class MyCallback : public IceUtil::Shared { public: void finished(const Ice::AsyncResultPtr& r) { EmployeesPrx e = EmployeesPrx::uncheckedCast(r->getProxy()); try { string name = e->end_getName(r); cout << "Name is: " << name << endl; } catch (const Ice::Exception& ex) { cerr << "Exception is: " << ex << endl; } } }; typedef IceUtil::Handle MyCallbackPtr; Note that your callback class must derive from . The callback method can have any name you prefer but its signatureIceUtil::Shared must match the preceding example. The implementation of your callback method must call the method. The proxy for the call is available via the method on the end_ getProxy that is passed by the Ice run time. The return type of is , so you must down-cast the proxy toAsyncResult getProxy Ice::ObjectPrx its correct type. (You should always use an to do this, otherwise you will send an additional message to the server to verifyuncheckedCast the proxy type.) Your callback method should catch and handle any exceptions that may be thrown by the method. If you allow an exception to escapeend_ from the callback method, the Ice run time produces a log entry by default and ignores the exception. (You can disable the log message by setting the property to zero.)Ice.Warn.AMICallback To inform the Ice run time that you want to receive a callback for the completion of the asynchronous call, you pass the callback instance to the method:begin_ C++ EmployeesPrx e = ...; MyCallbackPtr cb = new MyCallback; Ice::CallbackPtr d = Ice::newCallback(cb, &MyCallback::finished); e->begin_getName(99, d); Note the call to in this example. This helper function expects a smart pointer to your callback instance and a memberIce::newCallback function pointer that specifies your callback method.Ice 3.4.2 Documentation 233 Copyright © 2011, ZeroC, Inc. Using Cookies for Generic Completion Callbacks in C++ It is common for the method to require access to some state that is established by the code that calls the method. As anend_ begin_ example, consider an application that asynchronously starts a number of operations and, as each operation completes, needs to update different user interface elements with the results. In this case, the method knows which user interface element should receive thebegin_ update, and the method needs access to that element.end_ The API allows you to pass such state by providing a cookie. A cookie is an instance of a class that you write; the class can contain whatever data you want to pass, as well as any methods you may want to add to manipulate that data. The only requirement on the cookie class is that it must derive from . Here is an example implementation that stores a Ice::LocalObject . (We assume that this class provides whatever methods are needed by the method to update the display.)WidgetHandle end_ C++ class Cookie : public Ice::LocalObject { public: Cookie(WidgetHandle h) : _h(h) {} WidgetHandle getWidget() { return _h; } private: WidgetHandle _h; }; typedef IceUtil::Handle CookiePtr; When you call the method, you pass the appropriate cookie instance to inform the method how to update the display:begin_ end_ C++ // Make cookie for call to getName(99). CookiePtr cookie1 = new Cookie(widgetHandle1); // Make cookie for call to getName(42); CookiePtr cookie2 = new Cookie(widgetHandle2); // Invoke the getName operation with different cookies. e->begin_getName(99, getNameCB, cookie1); e->begin_getName(24, getNameCB, cookie2); The method can retrieve the cookie from the by calling . For this example, we assume that widgets have a end_ AsyncResult getCookie method that updates the relevant UI element:writeString C++ void MyCallback::getName(const Ice::AsyncResultPtr& r) { EmployeesPrx e = EmployeesPrx::uncheckedCast(r->getProxy()); CookiePtr cookie = CookiePtr::dynamicCast(r->getCookie()); try { string name = e->end_getName(r); cookie->getWidget()->writeString(name); } catch (const Ice::Exception& ex) { handleException(ex); } } The cookie provides a simple and effective way for you to pass state between the point where an operation is invoked and the point where its results are processed. Moreover, if you have a number of operations that share common state, you can pass the same cookie instance to multiple invocations.Ice 3.4.2 Documentation 234 Copyright © 2011, ZeroC, Inc. Type-Safe Completion Callbacks in C++ The is not entirely type-safe:generic callback API You must down-cast the return value of to the correct proxy type before you can call the method.getProxy end_ You must call the correct method to match the operation called by the method.end_ begin_ If you use a cookie, you must down-cast the cookie to the correct type before you can access the data inside the cookie. You must remember to catch exceptions when you call the method; if you forget to do this, you will not know that the operationend_ failed. slice2cpp generates an additional type-safe API that takes care of these chores for you. The type-safe API is provided as a template that works much like the class of the generic API, but requires strongly-typed method signatures.Ice::Callback To use type-safe callbacks, you must implement a callback class that provides two callback methods: A success callback that is called if the operation succeeds A failure callback that is called if the operation raises an exception As for the generic API, your callback class must derive from . Here is a callback class for an invocation of the IceUtil::Shared getName operation: C++ class MyCallback : public IceUtil::Shared { public: void getNameCB(const string& name) { cout << "Name is: " << name << endl; } void failureCB(const Ice::Exception& ex) { cerr << "Exception is: << ex << endl; } }; The callback methods can have any name you prefer and must have return type. The failure callback always has a single parameter ofvoid type . The success callback parameters depend on the operation signature. If the operation has non-const Ice::Exception& void return type, the first parameter of the success callback is the return value. The return value (if any) is followed by a parameter for each out-parameter of the corresponding Slice operation, in the order of declaration. To receive these callbacks, you instantiate your callback instance and specify the methods you have defined before passing a smart pointer to a callback wrapper instance to the method:begin_ C++ MyCallbackPtr cb = new MyCallback; Callback_Employees_getNamePtr getNameCB = newCallback_Employees_getName(cb, &MyCallback::getNameCB, &MyCallback::failureCB); Callback_Employees_getNumberPtr getNumberCB = newCallback_Employees_getNumber(cb, &MyCallback::getNumberCB, &MyCallback::failureCB); e->begin_getName(99, getNameCB); e->begin_getNumber("Fred", getNumberCB); Note how this code creates instances of two smart pointer types generated by named slice2cpp Callback_Employees_getNamePtr and . Each smart pointer points to a template instance that encapsulates your callback instanceCallback_Employees_getNumberPtr and two member function pointers for the callback methods. The name of this smart pointer type is formed as follows: ::Callback_ _Ptr Also note that the code uses helper functions to initialize the smart pointers. The first argument to the helper function is your callback instance, and the two following arguments are the success and failure member function pointers, respectively. The name of this helper function is formed as follows:Ice 3.4.2 Documentation 235 Copyright © 2011, ZeroC, Inc. ::newCallback_ _ It is legal to pass a null pointer as the success or failure callback. For the success callback, this is legal only for operations that have void return type and no out-parameters. This is useful if you do not care when the operation completes but want to know if the call failed. If you pass a null exception callback, the Ice run time will ignore any exception that is raised by the invocation. The type of the success and exception member function pointers is provided as and typedefs by the callbackResponse Exception template. For example, you can ignore exceptions for an invocation of as follows:getName C++ Callback_Employees_op::Exception nullException = 0; MyCallbackPtr cb = new MyCallback; Callback_Employees_getNamePtr getNameCB = newCallback_Employees_getName(cb, &MyCallback::getNameCB, nullException); e->begin_getName(99, getNameCB); // Ignores exceptions Using Cookies for Type-Safe Completion Callbacks in C++ The method optionally accepts a cookie as a trailing parameter. As for the generic API, you can use the cookie to share statebegin_ between the and methods. However, with the type-safe API, there is no need to down-cast the cookie. Instead, the cookiebegin_ end_ parameter that is passed to the method is strongly typed. Assuming that you have defined a class and smartend_ Cookie CookiePtr pointer, you can pass a cookie to the method as follows:begin_ C++ MyCallbackPtr cb = new MyCallback; Callback_Employees_getNamePtr getNameCB = newCallback_Employees_getName(cb, &MyCallback::getNameCB, &MyCallback::failureCB); CookiePtr cookie = new Cookie(widgetHandle); e->begin_getName(99, getNameCB, cookie); The callback methods of your callback class simply add the cookie parameter: C++ class MyCallback : public IceUtil::Shared { public: void getNameCB(const string& name, const CookiePtr& cookie) { cookie->getWidget()->writeString(name); } void failureCB(const Ice::Exception& ex, const CookiePtr& cookie) { cookie->getWidget()->writeError(ex.what()); } }; Asynchronous Oneway Invocations in C++ You can invoke operations via oneway proxies asynchronously, provided the operation has return type, does not have anyvoid out-parameters, and does not raise user exceptions. If you call the method on a oneway proxy for an operation that returns valuesbegin_ or raises a user exception, the method throws an .begin_ IceUtil::IllegalArgumentException For the generic API, the callback method looks exactly as for a twoway invocation. However, for oneway invocations, the Ice run time does not call the callback method unless the invocation raised an exception during the method ("on the way out").begin_Ice 3.4.2 Documentation 236 Copyright © 2011, ZeroC, Inc. For the type-safe API, the helper for operations is overloaded so you can omit the success callback. For example, herenewCallback void is how you could call asynchronously:ice_ping C++ MyCallbackPtr cb = new MyCallback; Ice::Callback_Object_ice_pingPtr callback = Ice::newCallback_Object_ice_ping(cb, &MyCallback::failureCB); p->begin_opVoid(callback); Flow Control in C++ Asynchronous method invocations never block the thread that calls the method: the Ice run time checks to see whether it can writebegin_ the request to the local transport. If it can, it does so immediately in the caller's thread. (In that case, returns true.) Alternatively, if the local transport does not have sufficient buffer space to accept theAsyncResult::sentSynchronously request, the Ice run time queues the request internally for later transmission in the background. (In that case, returns false.)AsyncResult::sentSynchronously This creates a potential problem: if a client sends many asynchronous requests at the time the server is too busy to keep up with them, the requests pile up in the client-side run time until, eventually, the client runs out of memory. The API provides a way for you to implement flow control by counting the number of requests that are queued so, if that number exceeds some threshold, the client stops invoking more operations until some of the queued operations have drained out of the local transport. For the generic API, you can create an additional callback method: C++ class MyCallback : public IceUtil::Shared { public: void finished(const Ice::AsyncResultPtr&); void sent(const Ice::AsyncResultPtr&); }; typedef IceUtil::Handle MyCallbackPtr; As with any other callback method, you are free to choose any name you like. For this example, the name of the callback method is .sent You inform the Ice run time that you want to be informed when a call has been passed to the local transport by specifying the methodsent as an additional parameter when you create the :Ice::Callback C++ EmployeesPrx e = ...; MyCallbackPtr cb = new MyCallback; Ice::CallbackPtr d = Ice::newCallback(cb, &MyCallback::finished, &MyCallback::sent); e->begin_getName(99, d); If the Ice run time can immediately pass the request to the local transport, it does so and invokes the method from the thread that callssent the method. On the other hand, if the run time has to queue the request, it calls the method from a different thread once it hasbegin_ sent written the request to the local transport. In addition, you can find out from the that is returned by the methodAsyncResult begin_ whether the request was sent synchronously or was queued, by calling .sentSynchronously For the generic API, the method has the following signature:sent C++ void sent(const Ice::AsyncResult&);Ice 3.4.2 Documentation 237 Copyright © 2011, ZeroC, Inc. For the type-safe API, there are two versions, one without and one with a cookie: C++ void sent(bool sentSynchronously); void sent(bool sentSynchronously, const & cookie); For the version with a cookie, is replaced with the actual type of the cookie smart pointer you passed to the method. begin_ The methods allow you to limit the number of queued requests by counting the number of requests that are queued and decrementingsent the count when the Ice run time passes a request to the local transport. Asynchronous Batch Requests in C++ Applications that send can either flush a batch explicitly or allow the Ice run time to flush automatically. The proxy method batched requests performs an immediate flush using the synchronous invocation model and may block the calling thread untilice_flushBatchRequests the entire message can be sent. Ice also provides asynchronous versions of this method so you can flush batch requests asynchronously. begin_ice_flushBatchRequests and are proxy methods that flush any batch requests queued byend_ice_flushBatchRequests that proxy. In addition, similar methods are available on the communicator and the object that is returned by Connection . These methods flush batch requests sent via the same communicator and via the same connection,AsyncResult::getConnection respectively. Concurrency Semantics for AMI in C++ The Ice run time always invokes your callback methods from a separate thread. This means that you can safely use a non-recursive mutex without risking deadlock. There is one exception to this rule: the run time calls the callback from the thread calling the methodsent begin_ if the request could be sent synchronously. In the callback, you know which thread is calling the callback by looking at the sent member or parameter, so you can take appropriate action to avoid a self-deadlock.sentSynchronously AMI Limitations in C++ AMI invocations cannot be sent using collocated optimization. If you attempt to invoke an AMI operation using a proxy that is configured to use , the Ice run time raises if the servant happens to be collocated; thecollocation optimization CollocationOptimizationException request is sent normally if the servant is not collocated. You can disable this optimization if necessary. See Also C++ Mapping for Classes Smart Pointers for Classes Request Contexts Batched Invocations Location TransparencyIce 3.4.2 Documentation 238 Copyright © 2011, ZeroC, Inc. slice2cpp Command-Line Options On this page: Command-Line Optionsslice2cpp --header-ext EXT --source-ext EXT --add-header [, ]HDR GUARD --include-dir DIR --impl --depend --dll-export SYMBOL --checksum --stream Include Directives Header Files Source Files slice2cpp Command-Line Options The Slice-to-C++ compiler, , offers the following command-line options in addition to the .slice2cpp standard options --header-ext EXT Changes the file extension for the generated header files from the default to the extension specified by .h EXT You can also change the header file extension with a global metadata directive: Slice [["cpp:header-ext:hpp"]] // ... Only one such directive can appear in each source file. If you specify a header extension on both the command line and with a metadata directive, the metadata directive takes precedence. This ensures that included Slice files that were compiled separately get the correct header extension (provided that the included Slice files contain a corresponding metadata directive). For example: Slice // File example.ice #include // ... Compiling this file with $ slice2cpp --header-ext=hpp -I/opt/Ice/include example.ice generates , but the directive in that file is for (not )example.hpp #include Ice/BuiltinSequences.h Ice/BuiltinSequences.hpp because contains the metadata directive .BuiltinSequences.ice [["cpp:header-ext:h"]] You normally will not need to use this metadata directive. The directive is necessary only if: You a Slice file in one of your own Slice files.#include The included Slice file is part of a library you link against. The library ships with the included Slice file's header. The library header uses a different header extension than your own code. For example, if the library uses as the header extension, but your own code uses , the library's Slice file should contain a .hpp .hIce 3.4.2 Documentation 239 Copyright © 2011, ZeroC, Inc. directive. (If the directive is missing, you can add it to the library's Slice file.)[["cpp:header?ext:hpp"]] --source-ext EXT Changes the file extension for the generated source files from the default to the extension specified by .cpp EXT --add-header [, ]HDR GUARD This option adds an include directive for the specified header at the beginning of the generated source file (preceding any other include directives). If is specified, the include directive is protected by the specified guard. For example, GUARD --add?header results in the following directives at the beginning of the generated source file:precompiled.h,__PRECOMPILED_H__ C++ #ifndef __PRECOMPILED_H__ #define __PRECOMPILED_H__ #include #endif The option can be repeated to create include directives for several files. As suggested by the preceding example, this option is useful mainly to integrate the generated code with a compiler's precompiled header mechanism. --include-dir DIR Modifies in source files to prepend the path name of each header file with the directory . directives#include DIR --impl Generate sample implementation files. This option will not overwrite an existing file. --depend Prints makefile dependency information to standard output. No code is generated when this option is specified. The output generally needs to be filtered before it can be included in a makefile; the Ice build system uses the script for this purpose.config/makedepend.py --dll-export SYMBOL Use to control DLL exports or imports. This option allows you to selectively export or import global symbols in the generated code.SYMBOL As an example, compiling a Slice definition with: $ slice2cpp --dll-export ENABLE_DLL x.ice results in the following additional code being generated into :x.h C++ #ifndef ENABLE_DLL # ifdef ENABLE_DLL_EXPORTS # define ENABLE_DLL ICE_DECLSPEC_EXPORT # else # define ENABLE_DLL ICE_DECLSPEC_IMPORT # endif #endif ICE_DECLSPEC_EXPORT and are platform-specific macros. For example, for Windows, they are defined as ICE_DECLSPEC_IMPORT and , respectively; for Solaris using version 5.5 or later, isdeclspec(dllexport) declspec(dllimport) CC ICE_DECLSPEC_EXPORT defined as , and is empty.global ICE_DECLSPEC_IMPORTIce 3.4.2 Documentation 240 Copyright © 2011, ZeroC, Inc. Similar definitions exist for other platforms. For platforms that do not have any concept of explicit export or import of shared library symbols, both macros are empty. The symbol name you specify on the command line ( in this example) is used by the generated code to export or import anyENABLE_DLL symbols that must be visible to code outside the generated compilation unit. The net effect is that, if you want to create a DLL that includes , but also want to use the generated types in compilation units outside the DLL, you can arrange for the relevant symbols to bex.cpp exported by compiling with .x.cpp -DENABLE_DLL_EXPORTS --checksum Generate for Slice definitions.checksums --stream Generate for Slice types.streaming helper functions Include Directives he directives generated by the Slice-to-C++ compiler can be a source of confusion if the semantics governing their generation are#include not well-understood. The generation of directives is influenced by the command-line options and ; these#include -I --include-dir options are discussed in more detail below. The option directs the translator to place all generated files in a particular--output-dir directory, but has no impact on the contents of the generated code. Given that the directives in header files and source files are generated using different semantics, we describe them in separate#include sections. Header Files In most cases, the compiler generates the appropriate directives by default. As an example, suppose file includes #include A.ice B.ice using the following statement: Slice // A.ice #include Assuming both files are in the current working directory, we run the compiler as shown below: $ slice2cpp -I. A.ice The generated file contains this directive:A.h #include C++ // A.h #include If the proper include paths are specified to the C++ compiler, everything should compile correctly. Similarly, consider the common case where includes from a subdirectory:A.ice B.ice Slice // A.ice #include Assuming both files are in the subdirectory, we run the compiler as shown below:incIce 3.4.2 Documentation 241 Copyright © 2011, ZeroC, Inc. $ slice2cpp -I. inc/A.ice The default output of the compiler produces this directive in :#include A.h C++ // A.h #include Again, it is the user's responsibility to ensure that the C++ compiler is configured to find during compilation.inc/B.h Now let us consider a more complex example, in which we do not want the directive in the header file to match that of the Slice#include file. This can be necessary when the organizational structure of the Slice files does not match the application's C++ code. In such a case, the user may need to relocate the generated files from the directory in which they were created, and the directives must be aligned#include with the new structure. For example, let us assume that is located in the subdirectory :B.ice slice/inc Slice // A.ice #include However, we do not want the subdirectory to appear in the directive generated in the header file, therefore we specify theslice #include additional compiler option :-Islice $ slice2cpp -I. -Islice slice/inc/A.ice The generated code demonstrates the impact of this extra option: C++ // A.h #include As you can see, the directives generated in header files are affected by the include paths that you specify when running the#include compiler. Specifically, the include paths are used to abbreviate the path name in generated directives.#include When translating an directive from a Slice file to a header file, the compiler compares each of the include paths against the path#include of the included file. If an include path matches the leading portion of the included file, the compiler removes that leading portion when generating the directive in the header file. If more than one include path matches, the compiler selects the one that results in the#include shortest path for the included file. For example, suppose we had used the following options when compiling :A.ice $ slice2cpp -I. -Islice -Islice/inc slice/inc/A.ice In this case, the compiler compares all of the include paths against the included file and generates the followingslice/inc/B.ice directive: C++ // A.h #include The option produces the shortest result, therefore the default path for the included file ( ) is replaced with -Islice/inc slice/inc/B.h .B.hIce 3.4.2 Documentation 242 Copyright © 2011, ZeroC, Inc. In general, the option plays two roles: it enables the preprocessor to locate included Slice files, and it provides you with a certain amount-I of control over the generated directives. In the last example above, the preprocessor locates using the#include slice/inc/B.ice include path specified by the option. The remaining options do not help the preprocessor locate included files; they are simply hints-I. -I to the compiler. Finally, we recommend using caution when specifying include paths. If the preprocessor is able to locate an included file via multiple include paths, it always uses the first include path that successfully locates the file. If you intend to modify the generated directives by#include specifying extra options, you must ensure that your include path hints match the include path selected by the preprocessor to locate the-I included file. As a general rule, you should avoid specifying include paths that enable the preprocessor to locate a file in multiple ways. Source Files By default, the compiler generates directives in source files using only the base name of the included file. This behavior is usually#include appropriate when the source file and header file reside in the same directory. For example, suppose includes from a subdirectory, as shown in the following snippet of :A.ice B.ice A.ice Slice // A.ice #include We generate the source file using this command: $ slice2cpp -I. inc/A.ice Upon examination, we see that the source file contains the following directive:#include C++ // A.cpp #include However, suppose that we wish to enforce a particular standard for generated directives so that they are compatible with our C++#include compiler's existing include path settings. In this case, we use the option to modify the generated code. For example,--include-dir consider the compiler command shown below: $ slice2cpp --include-dir src -I. inc/A.ice The source file now contains the following directive:#include C++ // A.cpp #include Any leading path in the included file is discarded as usual, and the value of the option is prepended.--include-dir See Also Using the Slice Compilers Using Slice Checksums in C++ Streaming InterfacesIce 3.4.2 Documentation 243 Copyright © 2011, ZeroC, Inc. Using Slice Checksums in C++ The Slice compilers can optionally generate of Slice definitions. For , the option causes the compiler tochecksums slice2cpp --checksum generate code in each C++ source file that accumulates checksums in a global map. A copy of this map can be obtained by calling a function defined in the header file :Ice/SliceChecksums.h C++ namespace Ice { Ice::SliceChecksumDict sliceChecksums(); } In order to verify a server's checksums, a client could simply compare the dictionaries using the equality operator. However, this is not feasible if it is possible that the server might be linked with more Slice definitions than the client. A more general solution is to iterate over the local checksums as demonstrated below: C++ Ice::SliceChecksumDict serverChecksums = ... Ice::SliceChecksumDict localChecksums = Ice::sliceChecksums(); for (Ice::SliceChecksumDict::const_iterator p = localChecksums.begin(); p != localChecksums.end(); ++p) { Ice::SliceChecksumDict::const_iterator q = serverChecksums.find(p->first); if (q == serverChecksums.end()) { // No match found for type id! } else if (p->second != q->second) { // Checksum mismatch! } } In this example, the client first verifies that the server's dictionary contains an entry for each Slice type ID, and then it proceeds to compare the checksums. See Also Slice Checksums slice2cpp Command-Line OptionsIce 3.4.2 Documentation 244 Copyright © 2011, ZeroC, Inc. Example of a File System Client in C++ This page presents a very simple client to access a server that implements the file system we developed in .Slice for a Simple File System The C++ code shown here hardly differs from the code you would write for an ordinary C++ program. This is one of the biggest advantages of using Ice: accessing a remote object is as easy as accessing an ordinary, local C++ object. This allows you to put your effort where you should, namely, into developing your application logic instead of having to struggle with arcane networking APIs. This is true for the server as well, meaning that you can develop distributed applications easily and efficiently.side We now have seen enough of the client-side C++ mapping to develop a complete client to access our remote file system. For reference, here is the Slice definition once more: Slice module Filesystem { interface Node { idempotent string name(); }; exception GenericError { string reason; }; sequence Lines; interface File extends Node { idempotent Lines read(); idempotent void write(Lines text) throws GenericError; }; sequence NodeSeq; interface Directory extends Node { idempotent NodeSeq list(); }; }; To exercise the file system, the client does a recursive listing of the file system, starting at the root directory. For each node in the file system, the client shows the name of the node and whether that node is a file or directory. If the node is a file, the client retrieves the contents of the file and prints them. The body of the client code looks as follows:Ice 3.4.2 Documentation 245 Copyright © 2011, ZeroC, Inc. 1. C++ #include #include #include #include using namespace std; using namespace Filesystem; static void listRecursive(const DirectoryPrx& dir, int depth = 0) { // ... } int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { // Create a communicator // ic = Ice::initialize(argc, argv); // Create a proxy for the root directory // Ice::ObjectPrx base = ic->stringToProxy("RootDir:default -p 10000"); if (!base) throw "Could not create proxy"; // Down-cast the proxy to a Directory proxy // DirectoryPrx rootDir = DirectoryPrx::checkedCast(base); if (!rootDir) throw "Invalid proxy"; // Recursively list the contents of the root directory // cout << "Contents of root directory:" << endl; listRecursive(rootDir); } catch (const Ice::Exception& ex) { cerr << ex << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } // Clean up // if (ic) ic->destroy(); return status; } The code includes a few header files: Ice/Ice.h: Always included in both client and server source files, provides definitions that are necessary for accessing the Ice run time. Filesystem.h: The header that is generated by the Slice compiler from the Slice definitions in .Filesystem.ice iostream: The client uses the library to produce its output.iostreamIce 3.4.2 Documentation 246 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 1. 2. 3. iterator: The implementation of uses an STL iterator.listRecursive The code adds declarations for the and namespaces.using std Filesystem The structure of the code in follows what we saw in . After initializing the run time, the client creates amain Hello World Application proxy to the root directory of the file system. For this example, we assume that the server runs on the local host and listens using the default protocol (TCP/IP) at port 10000. The object identity of the root directory is known to be .RootDir The client down-casts the proxy to and passes that proxy to , which prints the contents of the fileDirectoryPrx listRecursive system. Most of the work happens in :listRecursive C++ // Recursively print the contents of directory "dir" in // tree fashion. For files, show the contents of each file. // The "depth" parameter is the current nesting level // (for indentation). static void listRecursive(const DirectoryPrx& dir, int depth = 0) { string indent(++depth, '\t'); NodeSeq contents = dir->list(); for (NodeSeq::const_iterator i = contents.begin(); i != contents.end(); ++i) { DirectoryPrx dir = DirectoryPrx::checkedCast(*i); FilePrx file = FilePrx::uncheckedCast(*i); cout << indent << (*i)->name() << (dir ? " (directory):" : " (file):") << endl; if (dir) { listRecursive(dir, depth); } else { Lines text = file->read(); for (Lines::const_iterator j = text.begin(); j != text.end(); ++j) { cout << indent << "\t" << *j << endl; } } } } The function is passed a proxy to a directory to list, and an indent level. (The indent level increments with each recursive call and allows the code to print the name of each node at an indent level that corresponds to the depth of the tree at that node.) calls the listlistRecursive operation on the directory and iterates over the returned sequence of nodes: The code does a to narrow the proxy to a proxy, as well as an to narrow the checkedCast Node Directory uncheckedCast proxy to a proxy. Exactly one of those casts will succeed, so there is no need to call twice: if the Node File checkedCast Node , the code uses the returned by the ; if the fails, we that the is-a Directory DirectoryPrx checkedCast checkedCast know and, therefore, an is sufficient to get a .Node is-a File uncheckedCast FilePrx In general, if you know that a down-cast to a specific type will succeed, it is preferable to use an instead of a uncheckedCast because an does not incur any network traffic.checkedCast uncheckedCast The code prints the name of the file or directory and then, depending on which cast succeeded, prints or "(directory)" following the name."(file)" The code checks the type of the node: If it is a directory, the code recurses, incrementing the indent level. If it is a file, the code calls the operation on the file to retrieve the file contents and then iterates over the returnedread sequence of lines, printing each line. Assume that we have a small file system consisting of two files and a directory as follows:Ice 3.4.2 Documentation 247 Copyright © 2011, ZeroC, Inc. A small file system. The output produced by the client for this file system is: Contents of root directory: README (file): This file system contains a collection of poetry. Coleridge (directory): Kubla_Khan (file): In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea. Note that, so far, our client (and server) are not very sophisticated: The protocol and address information are hard-wired into the code. The client makes more remote procedure calls than strictly necessary; with minor redesign of the Slice definitions, many of these calls can be avoided. We will see how to address these shortcomings in our discussions of and .IceGrid object life cycle See Also Hello World Application Slice for a Simple File System Example of a File System Server in C++ Object Life Cycle IceGridIce 3.4.2 Documentation 248 Copyright © 2011, ZeroC, Inc. Server-Side Slice-to-C++ Mapping The mapping for Slice data types to C++ is identical on the client side and server side. This means that everything in Client-Side also applies to the server side. However, for the server side, there are a few additional things you need to know —Slice-to-C++ Mapping specifically how to: Initialize and finalize the server-side run time Implement servants Pass parameters and throw exceptions Create servants and register them with the Ice run time. Because the mapping for Slice data types is identical for clients and servers, the server-side mapping only adds a few additional mechanisms to the client side: a small API to initialize and finalize the run time, plus a few rules for how to derive servant classes from skeletons and how to register servants with the server-side run time. Although the examples we present are very simple, they accurately reflect the basics of writing an Ice server. Of course, for more sophisticated servers, you will be using , for example, to improve performance or scalability. However, these APIs are alladditional APIs described in Slice, so, to use these APIs, you need not learn any C++ mapping rules beyond those we describe here. Topics The Server-Side main Function in C++ Server-Side C++ Mapping for Interfaces Parameter Passing in C++ Raising Exceptions in C++ Object Incarnation in C++ Asynchronous Method Dispatch (AMD) in C++ Example of a File System Server in C++Ice 3.4.2 Documentation 249 Copyright © 2011, ZeroC, Inc. The Server-Side main Function in C++ On this page: A Basic Function in C++main The ClassIce::Application Using on the Client SideIce::Application Catching Signals in C++ and PropertiesIce::Application Limitations of Ice::Application The ClassIce::Service Member FunctionsIce::Service Unix Daemons Windows Services Logging ConsiderationsIce::Service A Basic Function in C++main The main entry point to the Ice run time is represented by the local Slice interface . As for the client side, you mustIce::Communicator initialize the Ice run time by calling before you can do anything else in your server. returns a smartIce::initialize Ice::initialize pointer to an instance of an :Ice::Communicator C++ int main(int argc, char* argv[]) { Ice::CommunicatorPtr ic = Ice::initialize(argc, argv); // ... } Ice::initialize accepts a C++ reference to and . The function scans the argument vector for any thatargc argv command-line options are relevant to the Ice run time; any such options are removed from the argument vector so, when returns, the onlyIce::initialize options and arguments remaining are those that concern your application. If anything goes wrong during initialization, throwsinitialize an exception. Ice::initialize has to permit other information to be passed to the Ice run time.additional overloads Before leaving your function, you must call . The operation is responsible for finalizing the Icemain Communicator::destroy destroy run time. In particular, waits for any operation implementations that are still executing in the server to complete. In addition, destroy ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as filedestroy descriptors and memory. Never allow your function to terminate without calling first; doing so has undefined behavior.main destroy The general shape of our server-side function is therefore as follows:mainIce 3.4.2 Documentation 250 Copyright © 2011, ZeroC, Inc. C++ #include int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { ic = Ice::initialize(argc, argv); // Server code here... } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } catch (const std::string& msg) { cerr << msg << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) { try { ic->destroy(); } catch (const Ice::Exception& e) { cerr << e << endl; status = 1; } } return status; } Note that the code places the call to in to a block and takes care to return the correct exit status to the operatingIce::initialize try system. Also note that an attempt to destroy the communicator is made only if the initialization succeeded. The handlers for and are in place as a convenience feature: if we encounter a fatal errorcatch const std::string & const char * condition anywhere in the server code, we can simply throw a string or a string literal containing an error message; this causes the stack to be unwound back to , at which point the error message is printed and, after destroying the communicator, terminates withmain main non-zero exit status. The ClassIce::Application The preceding structure for the function is so common that Ice offers a class, , that encapsulates all the correctmain Ice::Application initialization and finalization activities. The definition of the class is as follows (with some detail omitted for now):Ice 3.4.2 Documentation 251 Copyright © 2011, ZeroC, Inc. C++ namespace Ice { enum SignalPolicy { HandleSignals, NoSignalHandling }; class Application /* ... */ { public: Application(SignalPolicy = HandleSignals); virtual ~Application(); int main(int argc, char*[] argv); int main(int argc, char*[] argv, const char* config); int main(int argc, char*[] argv, const Ice::InitializationData& id); int main(int argc, char* const [] argv); int main(int argc, char* const [] argv, const char* config); int main(int argc, char* const [] argv, const Ice::InitializationData& id); int main(const Ice::StringSeq&); int main(const Ice::StringSeq&, const char* config); int main(const Ice::StringSeq&, const Ice::InitializationData& id); #ifdef _WIN32 int main(int argc, wchar_t*[] argv); int main(int argc, wchar_t*[] argv, const char* config); int main(int argc, wchar_t*[] argv, const Ice::InitializationData& id); #endif virtual int run(int, char*[]) = 0; static const char* appName(); static CommunicatorPtr communicator(); // ... }; } The intent of this class is that you specialize and implement the pure virtual method in your derived class.Ice::Application run Whatever code you would normally place in goes into the method instead. Using , our program looks asmain run Ice::Application follows: C++ #include class MyApplication : virtual public Ice::Application { public: virtual int run(int, char*[]) { // Server code here... return 0; } }; int main(int argc, char* argv[]) { MyApplication app; return app.main(argc, argv); } Note that is overloaded: you can pass a string sequence instead of an / pair. This is useful if you need to Application::main argc argv on the command line. You also can call with an optional file name or an parse application-specific property settings main structure.InitializationData If you pass a to , the property settings in this file are overridden by settings in a file identified by the configuration file name mainIce 3.4.2 Documentation 252 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. 7. environment variable (if defined). Property settings supplied on the take precedence over all other settings.ICE_CONFIG command line The function does the following:Application::main It installs an exception handler for . If your code fails to handle an Ice exception, prints theIce::Exception Application::main exception details on before returning with a non-zero return value.stderr It installs exception handlers for and . This allows you to terminate your server in responseconst std::string & const char* to a fatal error condition by throwing a or a string literal. prints the string on beforestd::string Application::main stderr returning a non-zero return value. It initializes (by calling ) and finalizes (by calling ) a communicator. You can getIce::initialize Communicator::destroy access to the communicator for your server by calling the static member function.communicator() It scans the argument vector for options that are relevant to the Ice run time and removes any such options. The argument vector that is passed to your method therefore is free of Ice-related options and only contains options and arguments that are specificrun to your application. It provides the name of your application via the static member function. The return value from this call is , so youappName argv[0] can get at from anywhere in your code by calling (which is often necessary for errorargv[0] Ice::Application::appName messages). It installs a that properly destroys the communicator.signal handler It installs a if the application has not already configured one. The per-process logger uses the value of the per-process logger property as a prefix for its messages and sends its output to the standard error channel. An application canIce.ProgramName also specify an .alternate logger Using ensures that your program properly finalizes the Ice run time, whether your server terminates normally or inIce::Application response to an exception or signal. We recommend that all your programs use this class; doing so makes your life easier. In addition, also provides features for signal handling and configuration that you do not have to implement yourself when you useIce::Application this class. Using on the Client SideIce::Application You can use for your clients as well: simply implement a class that derives from and place theIce::Application Ice::Application client code into its method. The advantage of this approach is the same as for the server side: ensures that therun Ice::Application communicator is destroyed correctly even in the presence of exceptions. Catching Signals in C++ The simple server we developed in had no way to shut down cleanly: we simply interrupted the server from theHello World Application command line to force it to exit. Terminating a server in this fashion is unacceptable for many real-life server applications: typically, the server has to perform some cleanup work before terminating, such as flushing database buffers or closing network connections. This is particularly important on receipt of a signal or keyboard interrupt to prevent possible corruption of database files or other persistent data. To make it easier to deal with signals, encapsulates the platform-independent provided byIce::Application signal handling capabilities the class . This allows you to cleanly shut down on receipt of a signal and to use the same source codeIceUtil::CtrlCHandler regardless of the underlying operating system and threading package: C++ namespace Ice { class Application : /* ... */ { public: // ... static void destroyOnInterrupt(); static void shutdownOnInterrupt(); static void ignoreInterrupt(); static void callbackOnInterrupt(); static void holdInterrupt(); static void releaseInterrupt(); static bool interrupted(); virtual void interruptCallback(int); }; } You can use under both Windows and Unix: for Unix, the member functions control the behavior of your application forIce::Application , , and ; for Windows, the member functions control the behavior of your application for , SIGINT SIGHUP SIGTERM CTRL_C_EVENT , , , and .CTRL_BREAK_EVENT CTRL_CLOSE_EVENT CTRL_LOGOFF_EVENT CTRL_SHUTDOWN_EVENT The functions behave as follows:Ice 3.4.2 Documentation 253 Copyright © 2011, ZeroC, Inc. destroyOnInterrupt This function creates an that destroys the communicator when one of the monitored signals is raised.IceUtil::CtrlCHandler This is the default behavior. shutdownOnInterrupt This function creates an that shuts down the communicator when one of the monitored signals isIceUtil::CtrlCHandler raised. ignoreInterrupt This function causes signals to be ignored. callbackOnInterrupt This function configures to invoke when a signal occurs, thereby giving the subclassIce::Application interruptCallback responsibility for handling the signal. Note that if the signal handler needs to terminate the program, you must call (instead of _exit ). This prevents global destructors from running which, depending on the activities of other threads in the program, could causeexit deadlock or assertion failures. holdInterrupt This function causes signals to be held. releaseInterrupt This function restores signal delivery to the previous disposition. Any signal that arrives after was called isholdInterrupt delivered when you call .releaseInterrupt interrupted This function returns if a signal caused the communicator to shut down, otherwise. This allows us to distinguishtrue false intentional shutdown from a forced shutdown that was caused by a signal. This is useful, for example, for logging purposes. interruptCallback A subclass overrides this function to respond to signals. The Ice run time may call this function concurrently with any other thread. If the function raises an exception, the Ice run time prints a warning on and ignores the exception.cerr By default, behaves as if was invoked, therefore our server function requires no changeIce::Application destroyOnInterrupt main to ensure that the program terminates cleanly on receipt of a signal. (You can disable the signal-handling functionality of by passing the enumerator to the constructor. In that case, signals retain their default behavior,Ice::Application NoSignalHandling that is, terminate the process.) However, we add a diagnostic to report the occurrence of a signal, so our function now looks like:main C++ #include class MyApplication : virtual public Ice::Application { public: virtual int run(int, char*[]) { // Server code here... if (interrupted()) cerr << appName() << ": terminating" << endl; return 0; } }; int main(int argc, char* argv[]) { MyApplication app; return app.main(argc, argv); } Note that, if your server is interrupted by a signal, the Ice run time waits for all currently executing operations to finish. This means that an operation that updates persistent state cannot be interrupted in the middle of what it was doing and cause partial update problems. Under Unix, if you handle signals with your own handler (by deriving a subclass from and calling Ice::Application ), the handler is invoked synchronously from a separate thread. This means that the handler can safely call into thecallbackOnInterruptIce 3.4.2 Documentation 254 Copyright © 2011, ZeroC, Inc. Ice run time or make system calls that are not async-signal-safe without fear of deadlock or data corruption. Note that Ice::Application blocks delivery of , , and . If your application calls , this means that the child process will also ignore theseSIGINT SIGHUP SIGTERM exec signals; if you need the default behavior of these signals in the 'd process, you must explicitly reset them to before calling exec SIG_DFL .exec Ice::Application and Properties Apart from the functionality shown in this section, also takes care of initializing the Ice run time with property values. Ice::Application allow you to configure the run time in various ways. For example, you can use properties to control things such as the thread poolProperties size or port number for a server. Limitations of Ice::Application Ice::Application is a singleton class that creates a single communicator. If you are using multiple communicators, you cannot use . Instead, you must structure your code as we saw in (taking care to always destroy theIce::Application Hello World Application communicators). The ClassIce::Service The class is very convenient for general use by Ice client and server applications. In some cases, however, anIce::Application application may need to run at the system level as a Unix daemon or Windows service. For these situations, Ice includes , aIce::Service singleton class that is comparable to but also encapsulates the low-level, platform-specific initialization and shutdownIce::Application procedures common to system services. The class is defined as follows:Ice::ServiceIce 3.4.2 Documentation 255 Copyright © 2011, ZeroC, Inc. C++ namespace Ice { class Service { public: Service(); virtual bool shutdown(); virtual void interrupt(); int main(int& argc, char* argv[], const Ice::InitializationData& = Ice::InitializationData()); int main(Ice::StringSeq& args, const Ice::InitializationData& = Ice::InitializationData()); Ice::CommunicatorPtr communicator() const; static Service* instance(); bool service() const; std::string name() const; bool checkSystem() const; int run(int& argc, char* argv[], const Ice::InitializationData&); #ifdef _WIN32 int main(int& argc, wchar_t* argv[], const InitializationData& = InitializationData()); void configureService(const std::string& name); #else void configureDaemon(bool changeDir, bool closeFiles, const std::string& pidFile); #endif virtual void handleInterrupt(int); protected: virtual bool start(int argc, char* argv[], int& status) = 0; virtual void waitForShutdown(); virtual bool stop(); virtual Ice::CommunicatorPtr initializeCommunicator( int& argc, char* argv[], const Ice::InitializationData&); virtual void syserror(const std::string& msg); virtual void error(const std::string& msg); virtual void warning(const std::string& msg); virtual void trace(const std::string& msg); virtual void print(const std::string& msg); void enableInterrupt(); void disableInterrupt(); // ... }; } At a minimum, an Ice application that uses the class must define a subclass and override the member function,Ice::Service start which is where the service must perform its startup activities, such as processing command-line arguments, creating an object adapter, and registering servants. The application's function must instantiate the subclass and typically invokes its member function, passingmain main the program's argument vector as parameters. The example below illustrates a minimal subclass:Ice::ServiceIce 3.4.2 Documentation 256 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 1. 2. 3. 4. 5. 6. 7. C++ #include class MyService : public Ice::Service { protected: virtual bool start(int, char*[], int&); private: Ice::ObjectAdapterPtr _adapter; }; bool MyService::start(int argc, char* argv[], int& status) { _adapter = communicator()->createObjectAdapter("MyAdapter"); _adapter->addWithUUID(new MyServantI); _adapter->activate(); status = EXIT_SUCCESS; return true; } int main(int argc, char* argv[]) { MyService svc; return svc.main(argc, argv); } The member function performs the following sequence of tasks:Service::main Scans the argument vector for reserved options that indicate whether the program should run as a system service and removes these options from the argument vector ( is adjusted accordingly). Additional reserved options are supported for administrativeargc tasks. Configures the program for running as a system service (if necessary) by invoking or , asconfigureService configureDaemon appropriate for the platform. Invokes the member function and returns its result.run Note that, as for , is overloaded to accept a string sequence instead of an / pair. This isApplication::main Service::main argc argv useful if you need to on the command line.parse application-specific property settings The member function executes the service in the steps shown below:Service::run Installs a .signal handler Invokes the member function to obtain a communicator. The communicator instance can be accessedinitializeCommunicator using the member function.communicator Invokes the member function. If returns to indicate failure, destroys the communicator and returnsstart start false run immediately using the exit status provided in .status Invokes the member function, which should block until is invoked.waitForShutdown shutdown Invokes the member function. If returns , considers the application to have terminated successfully.stop stop true run Destroys the communicator. Gracefully terminates the system service (if necessary). If an unhandled exception is caught by , a descriptive message is logged, the communicator is destroyed and the service isService::run terminated. Ice::Service Member Functions The virtual member functions in represent the points at which a subclass can intercept the service activities. All of the virtualIce::Service member functions (except ) have default implementations.start void handleInterrupt(int sig) Invoked by the when a signal occurs. The default implementation ignores the signal if it represents a logoff eventCtrlCHandler and the property is set to a value larger than zero, otherwise it invokes the member function.Ice.Nohup interrupt Ice::CommunicatorPtr initializeCommunicator(int & argc, char * argv[],Ice 3.4.2 Documentation 257 Copyright © 2011, ZeroC, Inc. const Ice::InitializationData & data) Initializes a communicator. The default implementation invokes and passes the given arguments.Ice::initialize void interrupt() Invoked by the signal handler to indicate a signal was received. The default implementation invokes the membershutdown function. bool shutdown() Causes the service to begin the shutdown process. The default implementation invokes on the communicator. Theshutdown subclass must return if shutdown was started successfully, and otherwise.true false bool start(int argc, char * argv[], int & status) Allows the subclass to perform its startup activities, such as scanning the provided argument vector for recognized command-line options, creating an object adapter, and registering servants. The subclass must return if startup was successful, and true false otherwise. The subclass can set an exit status via the parameter. This status is returned by .status main bool stop() Allows the subclass to clean up prior to termination. The default implementation does nothing but return . The subclass musttrue return if the service has stopped successfully, and otherwise.true false void syserror(const std::string & msg) void error(const std::string & msg) void warning(const std::string & msg) void trace(const std::string & msg) void print(const std::string & msg) Convenience functions for logging messages to the communicator's . The member function includes a descriptionlogger syserror of the system's current error code. void waitForShutdown() Waits indefinitely for the service to shut down. The default implementation invokes on the communicator.waitForShutdown The non-virtual member functions shown in the class definition are described below: bool checkSystem() const Returns true if the operating system supports Windows services or Unix daemons. This function returns false on Windows 95/98/ME. Ice::CommunicatorPtr communicator() const Returns the communicator used by the service, as created by .initializeCommunicator void configureDaemon(bool chdir, bool close, const std::string & pidFile) Configures the program to run as a Unix daemon. The parameter determines whether the daemon changes its workingchdir directory to the root directory. The parameter determines whether the daemon closes unnecessary file descriptors (i.e., stdin,close stdout, etc.). If a non-empty string is provided in the parameter, the daemon writes its process ID to the given file.pidFile void configureService(const std::string & name) Configures the program to run as a Windows service with the given name. void disableInterrupt() Disables the signal handling behavior in . When disabled, signals are ignored.Ice::Service void enableInterrupt() Enables the signal handling behavior in . When enabled, the occurrence of a signal causes the Ice::Service handleInterrupt member function to be invoked. static Service * instance() Returns the singleton instance.Ice::Service int main(int & argc, char * argv[], const Ice::InitializationData & data = Ice::InitializationData()) int main(Ice::StringSeq& args, const Ice::InitializationData& = Ice::InitializationData()); int main(int & argc, wchar_t * argv[], const Ice::InitializationData & data = Ice::InitializationData()) The primary entry point of the class. The tasks performed by this function are described earlier in this section. TheIce::Service function returns for success, for failure. For Windows, this function is overloaded to allow you toEXIT_SUCCESS EXIT_FAILURE pass a argument vector.wchar_t std::string name() const Returns the name of the service. If the program is running as a Windows service, the return value is the Windows service name,Ice 3.4.2 Documentation 258 Copyright © 2011, ZeroC, Inc. otherwise it returns the value of .argv[0] int run(int & argc, char * argv[], const Ice::InitializationData & data) Alternative entry point for applications that prefer a different style of service configuration. The program must invoke (Windows) or (Unix) in order to run as a service. The tasks performed by this functionconfigureService configureDaemon were described . The function normally returns or , but the method can also supply aearlier EXIT_SUCCESS EXIT_FAILURE start different value via its argument.status bool service() const Returns true if the program is running as a Windows service or Unix daemon, or false otherwise. Unix Daemons On Unix platforms, recognizes the following command-line options:Ice::Service --daemon Indicates that the program should run as a daemon. This involves the creation of a background child process in which performs its tasks. The parent process does not terminate until the child process has successfully invoked the Service::main member function.start This behavior avoids the uncertainty often associated with starting a daemon from a shell script, because it ensures that the command invocation does not complete until the daemon is ready to receive requests. Unless instructed otherwise, changes the current working directory of the child process to the root directory, andIce::Service closes all unnecessary file descriptors. Note that the file descriptors are not closed until after the communicator is initialized, meaning standard input, standard output, and standard error are available for use during this time. For example, the IceSSL plug-in may need to prompt for a passphrase on standard input, or Ice may print the child's process id on standard output if the property is set.Ice.PrintProcessId --pidfile FILE This option writes the process ID of the service into the specified . (This option requires .)FILE --daemon --noclose Prevents from closing unnecessary file descriptors. This can be useful during debugging and diagnosis because itIce::Service provides access to the output from the daemon's standard output and standard error. --nochdir Prevents from changing the current working directory.Ice::Service The and options can only be specified in conjunction with . These options are removed from the--noclose --nochdir --daemon argument vector that is passed to the member function.start Windows Services On Windows, recognizes the following command-line options:Ice::Service --service NAME Run as a Windows service named , which must already be installed. This option is removed from the argument vector that isNAME passed to the member function.start Installing and configuring a Windows service is outside the scope of the class. Ice includes a for installing its servicesIce::Service utility which you can use as a model for your own applications. The class supports the Windows service control codes and Ice::Service SERVICE_CONTROL_INTERROGATE SERVICE_CONTROL_STOP . Upon receipt of , invokes the member function.SERVICE_CONTROL_STOP Ice::Service shutdown Ice::Service Logging Considerations A service that uses a has several ways of configuring it:custom logger as a ,process-wide logger in the argument that is passed to ,InitializationData main by overriding the member function.initializeCommunicator On Windows, installs its own logger that uses the Windows event log if no custom logger is defined. TheIce::Service ApplicationIce 3.4.2 Documentation 259 Copyright © 2011, ZeroC, Inc. source name for the event log is the service's name unless a different value is specified using the property .Ice.EventLog.Source On Unix, the default Ice logger (which logs to the standard error output) is used when no other logger is configured. For daemons, this is not appropriate because the output will be lost. To change this, you can either implement a custom logger or set the property,Ice.UseSyslog which selects a logger implementation that logs to the facility. Alternatively, you can set the property to write logsyslog Ice.LogFile messages to a file. Note that may encounter errors before the communicator is initialized. In this situation, uses its defaultIce::Service Ice::Service logger unless a process-wide logger is configured. Therefore, even if a failing service is configured to use a different logger implementation, you may find useful diagnostic information in the event log (on Windows) or sent to standard error (on Unix).Application See Also Hello World Application Properties and Configuration Communicator Initialization Logger Facility Portable Signal Handling in C++ Windows ServicesIce 3.4.2 Documentation 260 Copyright © 2011, ZeroC, Inc. Server-Side C++ Mapping for Interfaces The server-side mapping for interfaces provides an up-call API for the Ice run time: by implementing virtual functions in a servant class, you provide the hook that gets the thread of control from the Ice server-side run time into your application code. On this page: Skeleton Classes in C++ Servant Classes in C++ Normal and Operations in C++idempotent Skeleton Classes in C++ On the client side, interfaces map to . On the server side, interfaces map to classes. A skeleton is a class that has aproxy classes skeleton pure virtual member function for each operation on the corresponding interface. For example, consider our for the Slice definition Node interface: Slice module Filesystem { interface Node { idempotent string name(); }; // ... }; The Slice compiler generates the following definition for this interface: C++ namespace Filesystem { class Node : virtual public Ice::Object { public: virtual std::string name(const Ice::Current& = Ice::Current()) = 0; // ... }; // ... } For the moment, we will ignore a number of other member functions of this class. The important points to note are: As for the client side, Slice modules are mapped to C++ namespaces with the same name, so the skeleton class definition is nested in the namespace .Filesystem The name of the skeleton class is the same as the name of the Slice interface ( ).Node The skeleton class contains a pure virtual member function for each operation in the Slice interface. The skeleton class is an abstract base class because its member functions are pure virtual. The skeleton class inherits from (which forms the root of the Ice object hierarchy).Ice::Object Servant Classes in C++ In order to provide an implementation for an Ice object, you must create a servant class that inherits from the corresponding skeleton class. For example, to create a servant for the interface, you could write:NodeIce 3.4.2 Documentation 261 Copyright © 2011, ZeroC, Inc. C++ #include // Slice-generated header class NodeI : public virtual Filesystem::Node { public: NodeI(const std::string&); virtual std::string name(const Ice::Current&); private: std::string _name; }; By convention, servant classes have the name of their interface with an -suffix, so the servant class for the interface is called .I Node NodeI (This is a convention only: as far as the Ice run time is concerned, you can choose any name you prefer for your servant classes.) Note that inherits from , that is, it derives from its skeleton class. It is a good idea to always use virtualNodeI Filesystem::Node inheritance when defining servant classes. Strictly speaking, virtual inheritance is necessary only for servants that implement interfaces that use multiple inheritance; however, the keyword does no harm and, if you add multiple inheritance to an interface hierarchyvirtual half-way through development, you do not have to go back and add a keyword to all your servant classes.virtual As far as Ice is concerned, the class must implement only a single member function: the pure virtual function that it inheritsNodeI name from its skeleton. This makes the servant class a concrete class that can be instantiated. You can add other member functions and data members as you see fit to support your implementation. For example, in the preceding definition, we added a member and a_name constructor. Obviously, the constructor initializes the member and the function returns its value:_name name C++ NodeI::NodeI(const std::string& name) : _name(name) { } std::string NodeI::name(const Ice::Current&) const { return _name; } Normal and Operations in C++idempotent The member function of the skeleton is not a member function. However, given that the operation does not modify thename NodeI const state of its object, it really should be a member function. We can achieve this by adding the metadata directive. Forconst ["cpp:const"] example: Slice interface Example { void normalOp(); idempotent void idempotentOp(); ["cpp:const"] idempotent void readonlyOp(); }; The skeleton class for this interface looks like this:Ice 3.4.2 Documentation 262 Copyright © 2011, ZeroC, Inc. C++ class Example : virtual public Ice::Object { public: virtual void normalOp(const Ice::Current& = Ice::Current()) = 0; virtual void idempotentOp(const Ice::Current& = Ice::Current()) = 0; virtual void readonlyOp(const Ice::Current& = Ice::Current()) const = 0; // ... }; Note that is mapped as a member function due to the metadata directive; normal and readonlyOp const ["cpp:const"] idempotent operations (without the metadata directive) are mapped as ordinary, non- member functions.const See Also Slice for a Simple File System C++ Mapping for Interfaces Parameter Passing in C++ Raising Exceptions in C++Ice 3.4.2 Documentation 263 Copyright © 2011, ZeroC, Inc. Parameter Passing in C++ For each parameter of a Slice operation, the C++ mapping generates a corresponding parameter for the virtual member function in the skeleton. In addition, every operation has an additional, trailing parameter of type . For example, the operation of the Ice::Current name interface has no parameters, but the member function of the skeleton class has a single parameter of type Node name Node . We will ignore this parameter for now.Ice::Current Parameter passing on the server side follows the rules for the client side: in-parameters are passed by value or reference.const out-parameters are passed by reference. return values are passed by value To illustrate the rules, consider the following interface that passes string parameters in all possible directions: Slice module M { interface Example { string op(string sin, out string sout); }; }; The generated skeleton class for this interface looks as follows: C++ namespace M { class Example : virtual public ::Ice::Object { public: virtual std::string op(const std::string&, std::string&, const Ice::Current& = Ice::Current()) = 0; // ... }; } As you can see, there are no surprises here. For example, we could implement as follows:op C++ std::string ExampleI::op(const std::string& sin, std::string& sout, const Ice::Current&) { cout << sin << endl; // In parameters are initialized sout = "Hello World!"; // Assign out parameter return "Done"; // Return a string } This code is in no way different from what you would normally write if you were to pass strings to and from a function; the fact that remote procedure calls are involved does not impact on your code in any way. The same is true for parameters of other types, such as proxies, classes, or dictionaries: the parameter passing conventions follow normal C++ rules and do not require special-purpose API calls or memory management. See Also Server-Side C++ Mapping for Interfaces Raising Exceptions in C++ The Current ObjectIce 3.4.2 Documentation 264 Copyright © 2011, ZeroC, Inc. 1. Raising Exceptions in C++ To throw an exception from an operation implementation, you simply instantiate the exception, initialize it, and throw it. For example: C++ void Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&) { // Try to write the file contents here... // Assume we are out of space... if (error) { Filesystem::GenericError e; e.reason = "file too large"; throw e; } }; No memory management issues arise in the presence of exceptions. Note that the Slice compiler never generates exception specifications for operations, regardless of whether the corresponding Slice operation definition has an exception specification or not. This is deliberate: C++ exception specifications do not add any value and are therefore not used by the Ice C++ mapping .[1] If you throw an arbitrary C++ exception (such as an or other unexpected type), the Ice run time catches the exception and then returnsint an to the client. Similarly, if you throw an "impossible" user exception (a user exception that is not listed in theUnknownException exception specification of the operation), the client receives an .UnknownUserException If you throw a run-time exception, such as , the client receives an . For that reason,MemoryLimitException UnknownLocalException you should never throw system exceptions from operation implementations. If you do, all the client will see is an UnknownLocalException , which does not tell the client anything useful. Three run-time exceptions are and not changed to when returned to thetreated specially UnknownLocalException client: , , and .ObjectNotExistException OperationNotExistException FacetNotExistException See Also Run-Time Exceptions C++ Mapping for Exceptions Server-Side C++ Mapping for Interfaces Parameter Passing in C++ References Sutter, H. 2002. . C/C++ Users Journal 20 (7): 59-64.A Pragmatic Look at Exception SpecificationsIce 3.4.2 Documentation 265 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. Object Incarnation in C++ Having created a servant class such as the rudimentary , you can instantiate the class to create a concrete servant that can classNodeI receive invocations from a client. However, merely instantiating a servant class is insufficient to incarnate an object. Specifically, to provide an implementation of an Ice object, you must follow the following steps: Instantiate a servant class. Create an identity for the Ice object incarnated by the servant. Inform the Ice run time of the existence of the servant. Pass a proxy for the object to a client so the client can reach it. On this page: Instantiating a C++ Servant Creating an Identity in C++ Activating a C++ Servant Servant Life Time and Reference Counts UUIDs as Identities in C++ Creating Proxies in C++ Proxies and Servant Activation in C++ Direct Proxy Creation in C++ Instantiating a C++ Servant Instantiating a servant means to allocate an instance on the heap: C++ NodePtr servant = new NodeI("Fred"); This code creates a new instance and assigns its address to a smart pointer of type . This works because NodeI on the heap NodePtr is derived from , so a smart pointer of type can also look after an instance of type . However, if we want toNodeI Node NodePtr NodeI invoke a member function of the derived class at this point, we have a problem: we cannot access member functions of the derived NodeI class through a smart pointer, only member functions of base class. (The C++ type rules prevent us from accessing aNodeI NodePtr Node member of a derived class through a pointer to a base class.) To get around this, we can modify the code as follows: C++ typedef IceUtil::Handle NodeIPtr; NodeIPtr servant = new NodeI("Fred"); This code makes use of the by defining as a smart pointer to instances. Whether you use a smartsmart pointer template NodeIPtr NodeI pointer of type or depends solely on whether you want to invoke a member function of the derived class; if youNodePtr NodeIPtr NodeI only want to invoke member functions that are defined in the skeleton base class, it is sufficient to use a and you need notNode NodePtr define the type.NodeIPtr Whether you use or , the advantages of using a smart pointer class should be obvious from the NodePtr NodeIPtr smart pointer : they make it impossible to accidentally leak memory.discussion Creating an Identity in C++ Each Ice object requires an identity. That identity must be unique for all servants using the same object adapter. The Ice object model assumes that all objects (regardless of their adapter) have a .globally unique identity An Ice object identity is a structure with the following Slice definition:Ice 3.4.2 Documentation 266 Copyright © 2011, ZeroC, Inc. 1. 2. 3. Slice module Ice { struct Identity { string name; string category; }; // ... }; The full identity of an object is the combination of both the and fields of the structure. For now, we will leave the name category Identity field as the empty string and simply use the field. (The field is most often used in conjunction with category name category servant .)locators To create an identity, we simply assign a key that identifies the servant to the field of the structure:name Identity C++ Ice::Identity id; id.name = "Fred"; // Not unique, but good enough for now Activating a C++ Servant Merely creating a servant instance does nothing: the Ice run time becomes aware of the existence of a servant only once you explicitly tell the object adapter about the servant. To activate a servant, you invoke the operation on the object adapter. Assuming that we haveadd access to the object adapter in the variable, we can write:_adapter C++ _adapter->add(servant, id); Note the two arguments to : the smart pointer to the servant and the object identity. Calling on the object adapter adds the servantadd add pointer and the servant's identity to the adapter's servant map and links the proxy for an Ice object to the correct servant instance in the server's memory as follows: The proxy for an Ice object, apart from addressing information, contains the identity of the Ice object. When a client invokes an operation, the object identity is sent with the request to the server. The object adapter receives the request, retrieves the identity, and uses the identity as an index into the servant map. If a servant with that identity is active, the object adapter retrieves the servant pointer from the servant map and dispatches the incoming request into the correct member function on the servant. Assuming that the object adapter is in the , client requests are dispatched to the servant as soon as you call .active state add Servant Life Time and Reference Counts Putting the preceding points together, we can write a simple function that instantiates and activates one of our servants. For thisNodeI example, we use a simple helper function called that creates and activates a servant with a given identity:activateServant C++ void activateServant(const string& name) { NodePtr servant = new NodeI(name); // Refcount == 1 Ice::Identity id; id.name = name; _adapter->add(servant, id); // Refcount == 2 } // Refcount == 1Ice 3.4.2 Documentation 267 Copyright © 2011, ZeroC, Inc. Note that we create the servant on the heap and that, once returns, we lose the last remaining handle to the servantactivateServant (because the variable goes out of scope). The question is, what happens to the heap-allocated servant instance? The answer liesservant in the smart pointer semantics: When the new servant is instantiated, its reference count is initialized to 0. Assigning the servant's address to the smart pointer increments the servant's reference count to 1.servant Calling passes the smart pointer to the object adapter which keeps a copy of the handle internally. This incrementsadd servant the reference count of the servant to 2. When returns, the destructor of the variable decrements the reference count of the servant to 1.activateServant servant The net effect is that the servant is retained on the heap with a reference count of 1 for as long as the servant is in the servant map of its object adapter. (If we deactivate the servant, that is, remove it from the servant map, the reference count drops to zero and the memory occupied by the servant is reclaimed; we discuss these life cycle issues in .)Object Life Cycle UUIDs as Identities in C++ The Ice object model assumes that object identities are globally unique. One way of ensuring that uniqueness is to use UUIDs (Universally Unique Identifiers) as identities. The namespace contains a to create such identities:IceUtil helper function C++ #include #include using namespace std; int main() { cout << IceUtil::generateUUID() << endl; } When executed, this program prints a unique string such as . Each call to 5029a22c-e333-4f87-86b1-cd5e0fcce509 generateUUID creates a string that differs from all previous ones. You can use a UUID such as this to create object identities. For convenience, the object adapter has an operation thataddWithUUID generates a UUID and adds a servant to the servant map in a single step. Using this operation, we can rewrite the code shown likeearlier this: C++ void activateServant(const string& name) { NodePtr servant = new NodeI(name); _adapter->addWithUUID(servant); } Creating Proxies in C++ Once we have activated a servant for an Ice object, the server can process incoming client requests for that object. However, clients can only access the object once they hold a proxy for the object. If a client knows the server's address details and the object identity, it can create a proxy from a string, as we saw in our first example in . However, creation of proxies by the client in thisHello World Application manner is usually only done to allow the client access to initial objects for bootstrapping. Once the client has an initial proxy, it typically obtains further proxies by invoking operations. The object adapter contains all the details that make up the information in a proxy: the addressing and protocol information, and the object identity. The Ice run time offers a number of ways to create proxies. Once created, you can pass a proxy to the client as the return value or as an out-parameter of an operation invocation. Proxies and Servant Activation in C++Ice 3.4.2 Documentation 268 Copyright © 2011, ZeroC, Inc. The and servant activation operations on the object adapter return a proxy for the corresponding Ice object. This meansadd addWithUUID we can write: C++ typedef IceUtil::Handle NodeIPtr; NodeIPtr servant = new NodeI(name); NodePrx proxy = NodePrx::uncheckedCast(_adapter->addWithUUID(servant)); // Pass proxy to client... Here, both activates the servant and returns a proxy for the Ice object incarnated by that servant in a single step.addWithUUID Note that we need to use an here because returns a proxy of type .uncheckedCast addWithUUID Ice::ObjectPrx Direct Proxy Creation in C++ The object adapter offers an operation to create a proxy for a given identity: Slice module Ice { local interface ObjectAdapter { Object* createProxy(Identity id); // ... }; }; Note that creates a proxy for a given identity whether a servant is activated with that identity or not. In other words, proxiescreateProxy have a life cycle that is quite independent from the life cycle of servants: C++ Ice::Identity id; id.name = IceUtil::generateUUID(); ObjectPrx o = _adapter->createProxy(id); This creates a proxy for an Ice object with the identity returned by . Obviously, no servant yet exists for that object so, if wegenerateUUID return the proxy to a client and the client invokes an operation on the proxy, the client will receive an . (WeObjectNotExistException examine these life cycle issues in more detail in .)Object Life Cycle See Also Hello World Application Object Adapter States Servant Locators Object Life Cycle The C++ generateUUID FunctionIce 3.4.2 Documentation 269 Copyright © 2011, ZeroC, Inc. 1. Asynchronous Method Dispatch (AMD) in C++ The number of simultaneous synchronous requests a server is capable of supporting is determined by the number of threads in the server's . If all of the threads are busy dispatching long-running operations, then no threads are available to process new requests andthread pool therefore clients may experience an unacceptable lack of responsiveness. Asynchronous Method Dispatch (AMD), the server-side equivalent of , addresses this scalability issue. Using AMD, a server can receiveAMI a request but then suspend its processing in order to release the dispatch thread as soon as possible. When processing resumes and the results are available, the server sends a response explicitly using a callback object provided by the Ice run time. AMD is transparent to the client, that is, there is no way for a client to distinguish a request that, in the server, is processed synchronously from a request that is processed asynchronously. In practical terms, an AMD operation typically queues the request data (i.e., the callback object and operation arguments) for later processing by an application thread (or thread pool). In this way, the server minimizes the use of dispatch threads and becomes capable of efficiently supporting thousands of simultaneous clients. An alternate use case for AMD is an operation that requires further processing after completing the client's request. In order to minimize the client's delay, the operation returns the results while still in the dispatch thread, and then continues using the dispatch thread for additional work. On this page: Enabling AMD with Metadata in C++ AMD Mapping in C++ AMD Exceptions in C++ AMD Example in C++ Enabling AMD with Metadata in C++ To enable asynchronous dispatch, you must add an metadata directive to your Slice definitions. The directive applies at the["amd"] interface and the operation level. If you specify at the interface level, all operations in that interface use asynchronous dispatch; if["amd"] you specify for an individual operation, only that operation uses asynchronous dispatch. In either case, the metadata directive ["amd"] synchronous dispatch, that is, a particular operation implementation must use synchronous or asynchronous dispatch and cannotreplaces use both. Consider the following Slice definitions: Slice ["amd"] interface I { bool isValid(); float computeRate(); }; interface J { ["amd"] void startProcess(); int endProcess(); }; In this example, both operations of interface use asynchronous dispatch, whereas, for interface , uses asynchronousI J startProcess dispatch and uses synchronous dispatch.endProcess Specifying metadata at the operation level (rather than at the interface or class level) minimizes the amount of generated code and, more importantly, minimizes complexity: although the asynchronous model is more flexible, it is also more complicated to use. It is therefore in your best interest to limit the use of the asynchronous model to those operations that need it, while using the simpler synchronous model for the rest. AMD Mapping in C++ The C++ mapping emits the following code for each AMD operation: A callback class used by the implementation to notify the Ice run time about the completion of an operation. The name of this class is formed using the pattern _ . For example, an operation named defined in interface results in a class named AMD_class op foo I . The class is generated in the same scope as the interface or class containing the operation. Several methods areAMD_I_fooIce 3.4.2 Documentation 270 Copyright © 2011, ZeroC, Inc. 1. 2. provided: void ice_response(); The method allows the server to report the successful completion of the operation. If the operation has aice_response non- return type, the first parameter to is the return value. Parameters corresponding to thevoid ice_response operation's parameters follow the return value, in the order of declaration.out void ice_exception(const std::exception &); This version of allows the server to raise any standard exception, Ice run-time exception, or Ice userice_exception exception. void ice_exception(); This version of allows the server to report an .ice_exception UnknownException Neither nor throw any exceptions to the caller.ice_response ice_exception The dispatch method, whose name has the suffix . This method has a return type. The first parameter is a smart_async void pointer to an instance of the callback class described above. The remaining parameters comprise the in-parameters of the operation, in the order of declaration. For example, suppose we have defined the following operation: Slice interface I { ["amd"] int foo(short s, out long l); }; The callback class generated for operation is shown below:foo C++ class AMD_I_foo : public ... { public: void ice_response(Ice::Int, Ice::Long); void ice_exception(const std::exception&); void ice_exception(); }; The dispatch method for asynchronous invocation of operation is generated as follows:foo C++ void foo_async(const AMD_I_fooPtr&, Ice::Short); AMD Exceptions in C++ There are two processing contexts in which the logical implementation of an AMD operation may need to report an exception: the dispatch thread (the thread that receives the invocation), and the response thread (the thread that sends the response). These are not necessarily two different threads: it is legal to send the response from the dispatch thread. Although we recommend that the callback object be used to report all exceptions to the client, it is legal for the implementation to raise an exception instead, but only from the dispatch thread. As you would expect, an exception raised from a response thread cannot be caught by the Ice run time; the application's run-time environment determines how such an exception is handled. Therefore, a response thread must ensure that it traps all exceptions and sends the appropriate response using the callback object. Otherwise, if a response thread is terminated by an uncaught exception, the request may never be completed and the client might wait indefinitely for a response. Whether raised in a dispatch thread or reported via the callback object, user exceptions are and local exceptions may undergo validated .translation AMD Example in C++Ice 3.4.2 Documentation 271 Copyright © 2011, ZeroC, Inc. To demonstrate the use of AMD in Ice, let us define the Slice interface for a simple computational engine: Slice module Demo { sequence Row; sequence Grid; exception RangeError {}; interface Model { ["amd"] Grid interpolate(Grid data, float factor) throws RangeError; }; }; Given a two-dimensional grid of floating point values and a factor, the operation returns a new grid of the same size with theinterpolate values interpolated in some interesting (but unspecified) way. Our servant class derives from and supplies a definition for the method:Demo::Model interpolate_async C++ class ModelI : virtual public Demo::Model, virtual public IceUtil::Mutex { public: virtual void interpolate_async( const Demo::AMD_Model_interpolatePtr&, const Demo::Grid&, Ice::Float, const Ice::Current&); private: std::list _jobs; }; The implementation of uses synchronization to safely record the callback object and arguments in a that isinterpolate_async Job added to a queue: C++ void ModelI::interpolate_async( const Demo::AMD_Model_interpolatePtr& cb, const Demo::Grid& data, Ice::Float factor, const Ice::Current& current) { IceUtil::Mutex::Lock sync(*this); JobPtr job = new Job(cb, data, factor); _jobs.push_back(job); } After queuing the information, the operation returns control to the Ice run time, making the dispatch thread available to process another request. An application thread removes the next from the queue and invokes to perform the interpolation. is defined asJob execute Job follows:Ice 3.4.2 Documentation 272 Copyright © 2011, ZeroC, Inc. C++ class Job : public IceUtil::Shared { public: Job(const Demo::AMD_Model_interpolatePtr&, const Demo::Grid&, Ice::Float); void execute(); private: bool interpolateGrid(); Demo::AMD_Model_interpolatePtr _cb; Demo::Grid _grid; Ice::Float _factor; }; typedef IceUtil::Handle JobPtr; The implementation of uses (not shown) to perform the computational work:execute interpolateGrid C++ Job::Job(const Demo::AMD_Model_interpolatePtr& cb, const Demo::Grid& grid, Ice::Float factor) : _cb(cb), _grid(grid), _factor(factor) { } void Job::execute() { if (!interpolateGrid()) { _cb?>ice_exception(Demo::RangeError()); return; } _cb?>ice_response(_grid); } If returns , then is invoked to indicate that a range error has occurred. The statementinterpolateGrid false ice_exception return following the call to is necessary because does not throw an exception; it only marshals the exceptionice_exception ice_exception argument and sends it to the client. If interpolation was successful, is called to send the modified grid back to the client.ice_response See Also Asynchronous Method Invocation (AMI) in C++ The Ice Threading Model User Exceptions Run-Time ExceptionsIce 3.4.2 Documentation 273 Copyright © 2011, ZeroC, Inc. Example of a File System Server in C++ This page presents the source code for a C++ server that implements our and communicates with the we wrote earlier. Thefile system client code is fully functional, apart from the required .interlocking for threads The server is remarkably free of code that relates to distribution: most of the server code is simply application logic that would be present just the same for a non-distributed version. Again, this is one of the major advantages of Ice: distribution concerns are kept away from application code so that you can concentrate on developing application logic instead of networking infrastructure. The server code shown here is not quite correct as it stands: if two clients access the same file in parallel, each via a different thread, one thread may read the data member while another thread updates it. Obviously, if that_lines happens, we may write or return garbage or, worse, crash the server. However, it is trivial to make the and read write operations thread-safe: a single data member and two lines of source code are sufficient to achieve this. We discuss how to write thread-safe servant implementations in .Threads and Concurrency with C++ On this page: Implementing a File System Server in C++ Server Program in C++main Servant Class Definitions in C++ The Servant Implementation in C++ Implementing FileI Implementing DirectoryI Implementing NodeI Implementing a File System Server in C++ We have now seen enough of the server-side C++ mapping to implement a server for our . (You may find it useful to review thesefile system Slice definitions before studying the source code.) Our server is composed of two source files: Server.cpp This file contains the server main program. FilesystemI.cpp This file contains the implementation for the file system servants. Server Program in C++main Our server main program, in the file , uses the class. The method installs a signal handler, createsServer.cpp Ice::Application run an object adapter, instantiates a few servants for the directories and files in the file system, and then activates the adapter. This leads to a program as follows:main C++ #include #include using namespace std; using namespace Filesystem; class FilesystemApp : virtual public Ice::Application { public: virtual int run(int, char*[]) { // Terminate cleanly on receipt of a signal // shutdownOnInterrupt(); // Create an object adapter. // Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapterWithEndpoints(Ice 3.4.2 Documentation 274 Copyright © 2011, ZeroC, Inc. "SimpleFilesystem", "default -p 10000"); // Create the root directory (with name "/" and no parent) // DirectoryIPtr root = new DirectoryI(communicator(), "/", 0); root->activate(adapter); // Create a file called "README" in the root directory // FileIPtr file = new FileI(communicator(), "README", root); Lines text; text.push_back("This file system contains a collection of poetry."); file->write(text); file->activate(adapter); // Create a directory called "Coleridge" // in the root directory // DirectoryIPtr coleridge = new DirectoryI(communicator(), "Coleridge", root); coleridge->activate(adapter); // Create a file called "Kubla_Khan" // in the Coleridge directory // file = new FileI(communicator(), "Kubla_Khan", coleridge); text.erase(text.begin(), text.end()); text.push_back("In Xanadu did Kubla Khan"); text.push_back("A stately pleasure-dome decree:"); text.push_back("Where Alph, the sacred river, ran"); text.push_back("Through caverns measureless to man"); text.push_back("Down to a sunless sea."); file->write(text); file->activate(adapter); // All objects are created, allow client requests now // adapter->activate(); // Wait until we are done // communicator()->waitForShutdown(); if (interrupted()) { cerr << appName() << ": received signal, shutting down" << endl; } return 0; }; }; int main(int argc, char* argv[]) { FilesystemApp app;Ice 3.4.2 Documentation 275 Copyright © 2011, ZeroC, Inc. return app.main(argc, argv); } There is quite a bit of code here, so let us examine each section in detail: C++ #include #include using namespace std; using namespace Filesystem; The code includes the header file . That file includes as well as the header file that is generated by the SliceFilesystemI.h Ice/Ice.h compiler, . Because we are using , we need to include as well.Filesystem.h Ice::Application Ice/Application.h Two declarations, for the namespaces and , permit us to be a little less verbose in the source code.using std Filesystem The next part of the source code is the definition of , which derives from and contains the mainFilesystemApp Ice::Application application logic in its method:runIce 3.4.2 Documentation 276 Copyright © 2011, ZeroC, Inc. C++ class FilesystemApp : virtual public Ice::Application { public: virtual int run(int, char*[]) { // Terminate cleanly on receipt of a signal // shutdownOnInterrupt(); // Create an object adapter. // Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapterWithEndpoints( "SimpleFilesystem", "default -p 10000"); // Create the root directory (with name "/" and no parent) // DirectoryIPtr root = new DirectoryI(communicator(), "/", 0); root->activate(adapter); // Create a file called "README" in the root directory // FileIPtr file = new FileI(communicator(), "README", root); Lines text; text.push_back("This file system contains a collection of poetry."); file->write(text); file->activate(adapter); // Create a directory called "Coleridge" // in the root directory // DirectoryIPtr coleridge = new DirectoryI(communicator(), "Coleridge", root); coleridge->activate(adapter); // Create a file called "Kubla_Khan" // in the Coleridge directory // file = new FileI(communicator(), "Kubla_Khan", coleridge); text.erase(text.begin(), text.end()); text.push_back("In Xanadu did Kubla Khan"); text.push_back("A stately pleasure-dome decree:"); text.push_back("Where Alph, the sacred river, ran"); text.push_back("Through caverns measureless to man"); text.push_back("Down to a sunless sea."); file->write(text); file->activate(adapter); // All objects are created, allow client requests now // adapter->activate(); // Wait until we are done // communicator()->waitForShutdown(); if (interrupted()) { cerr << appName() << ": received signal, shutting down" << endl; } return 0; }; }; Much of this code is boiler plate that we saw previously: we create an object adapter, and, towards the end, activate the object adapter and call .waitForShutdown The interesting part of the code follows the adapter creation: here, the server instantiates a few nodes for our file system to create theIce 3.4.2 Documentation 277 Copyright © 2011, ZeroC, Inc. structure shown below: A small file system. As we will see shortly, the servants for our directories and files are of type and , respectively. The constructor for eitherDirectoryI FileI type of servant accepts three parameters: the communicator, the name of the directory or file to be created, and a handle to the servant for the parent directory. (For the root directory, which has no parent, we pass a null parent handle.) Thus, the statement C++ DirectoryIPtr root = new DirectoryI(communicator(), "/", 0); creates the root directory, with the name and no parent directory. Note that we use the to hold the return value from "/" smart pointer class ; that way, we avoid any memory management issues. The types and are defined as follows in a headernew DirectoryIPtr FileIPtr file :FilesystemI.h C++ typedef IceUtil::Handle DirectoryIPtr; typedef IceUtil::Handle FileIPtr; Here is the code that establishes the structure in the illustration above.Ice 3.4.2 Documentation 278 Copyright © 2011, ZeroC, Inc. C++ // Create the root directory (with name "/" and no parent) // DirectoryIPtr root = new DirectoryI(communicator(), "/", 0); root->activate(adapter); // Create a file called "README" in the root directory // FileIPtr file = new FileI(communicator(), "README", root); Lines text; text.push_back("This file system contains a collection of poetry."); file->write(text); file->activate(adapter); // Create a directory called "Coleridge" // in the root directory // DirectoryIPtr coleridge = new DirectoryI(communicator(), "Coleridge", root); coleridge->activate(adapter); // Create a file called "Kubla_Khan" // in the Coleridge directory // file = new FileI(communicator(), "Kubla_Khan", coleridge); text.erase(text.begin(), text.end()); text.push_back("In Xanadu did Kubla Khan"); text.push_back("A stately pleasure-dome decree:"); text.push_back("Where Alph, the sacred river, ran"); text.push_back("Through caverns measureless to man"); text.push_back("Down to a sunless sea."); file->write(text); file->activate(adapter); We first create the root directory and a file within the root directory. (Note that we pass the handle to the root directory as the parentREADME pointer when we create the new node of type .)FileI After creating each servant, the code calls on the servant. (We will see the definition of this member function shortly.) The activate member function adds the servant to the ASM.activate The next step is to fill the file with text: C++ FileIPtr file = new FileI(communicator(), "README", root); Lines text; text.push_back("This file system contains a collection of poetry."); file->write(text); file->activate(adapter); Recall that map to STL vectors. The Slice type is a sequence of strings, so the C++ type is a vector ofSlice sequences Lines Lines strings; we add a line of text to our file by calling on that vector.README push_back Finally, we call the Slice operation on our servant by simply writing:write FileI C++ file->write(text); This statement is interesting: the server code invokes an operation on one of its own servants. Because the call happens via a smart class pointer (of type ) and not via a proxy (of type ), the Ice run time does not know that this call is even taking place — such aFilePtr FilePrx direct call into a servant is not mediated by the Ice run time in any way and is dispatched as an ordinary C++ function call.Ice 3.4.2 Documentation 279 Copyright © 2011, ZeroC, Inc. In similar fashion, the remainder of the code creates a subdirectory called and, within that directory, a file called toColeridge Kubla_Khan complete the structure in the above illustration. Servant Class Definitions in C++ We must provide servants for the concrete interfaces in our Slice specification, that is, we must provide servants for the and File interfaces in the C++ classes and . This means that our servant classes might look as follows:Directory FileI DirectoryI C++ namespace Filesystem { class FileI : virtual public File { // ... }; class DirectoryI : virtual public Directory { // ... }; } This leads to the C++ class structure as shown: File system servants using interface inheritance. The shaded classes in the illustration above are skeleton classes and the unshaded classes are our servant implementations. If we implement our servants like this, must implement the pure virtual operations it inherits from the skeleton ( and ), asFileI File read write well as the operation it inherits from the skeleton ( ). Similarly, must implement the pure virtual function it inheritsNode name DirectoryI from the skeleton ( ), as well as the operation it inherits from the skeleton ( ). Implementing the servants in thisDirectory list Node name way uses interface inheritance from because no implementation code is inherited from that class.Node Alternatively, we can implement our servants using the following definitions: C++ namespace Filesystem { class NodeI : virtual public Node { // ... }; class FileI : virtual public File, virtual public NodeI { // ... }; class DirectoryI : virtual public Directory, virtual public NodeI { // ... }; } This leads to the C++ class structure shown:Ice 3.4.2 Documentation 280 Copyright © 2011, ZeroC, Inc. File system servants using implementation inheritance. In this implementation, is a concrete base class that implements the operation it inherits from the skeleton. and NodeI name Node FileI use multiple inheritance from and their respective skeletons, that is, and use implementationDirectoryI NodeI FileI DirectoryI inheritance from their base class.NodeI Either implementation approach is equally valid. Which one to choose simply depends on whether we want to re-use common code provided by . For the implementation that follows, we have chosen the second approach, using implementation inheritance.NodeI Given the structure in the above illustration and the operations we have defined in the Slice definition for our file system, we can add these operations to the class definition for our servants: C++ namespace Filesystem { class NodeI : virtual public Node { public: virtual std::string name(const Ice::Current&); }; class FileI : virtual public File, virtual public NodeI { public: virtual Lines read(const Ice::Current&); virtual void write(const Lines&, const Ice::Current&); }; class DirectoryI : virtual public Directory, virtual public NodeI { public: virtual NodeSeq list(const Ice::Current&); }; } This simply adds signatures for the operation implementations to each class. Note that the signatures must exactly match the operation signatures in the generated skeleton classes — if they do not match exactly, you end up overloading the pure virtual function in the base class instead of overriding it, meaning that the servant class cannot be instantiated because it will still be abstract. To avoid signature mismatches, you can copy the signatures from the generated header file ( ), or you can use the option with Filesystem.h --impl to generate header and implementation files that you can add your application code to.slice2cpp Now that we have the basic structure in place, we need to think about other methods and data members we need to support our servant implementation. Typically, each servant class hides the copy constructor and assignment operator, and has a constructor to provide initial state for its data members. Given that all nodes in our file system have both a name and a parent directory, this suggests that the NodeI class should implement the functionality relating to tracking the name of each node, as well as the parent-child relationships:Ice 3.4.2 Documentation 281 Copyright © 2011, ZeroC, Inc. C++ namespace Filesystem { class DirectoryI; typedef IceUtil::Handle DirectoryIPtr; class NodeI : virtual public Node { public: virtual std::string name(const Ice::Current&); NodeI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); void activate(const Ice::ObjectAdapterPtr&); private: std::string _name; Ice::Identity _id; DirectoryIPtr _parent; NodeI(const NodeI&); // Copy forbidden void operator=(const NodeI&); // Assignment forbidden }; } The class has a private data member to store its name (of type ) and its parent directory (of type ).NodeI std::string DirectoryIPtr The constructor accepts parameters that set the value of these data members. For the root directory, by convention, we pass a null handle to the constructor to indicate that the root directory has no parent. The constructor also requires the communicator to be passed to it. This is necessary because the constructor creates the identity for the servant, which requires access to the communicator. The memberactivate function adds the servant to the ASM (which requires access to the object adapter) and connects the child to its parent. The servant class must store the contents of its file, so it requires a data member for this. We can conveniently use the generated FileI type (which is a ) to hold the file contents, one string for each line. Because inherits from Lines std::vector FileI , it also requires a constructor that accepts the communicator, file name, and parent directory, leading to the following class definition:NodeI C++ namespace Filesystem { class FileI : virtual public File, virtual public NodeI { public: virtual Lines read(const Ice::Current&); virtual void write(const Lines&, const Ice::Current&); FileI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); private: Lines _lines; }; } For directories, each directory must store its list of child notes. We can conveniently use the generated type (which is a NodeSeq ) to do this. Because inherits from , we need to add a constructor to initialize the directory namevector DirectoryI NodeI and its parent directory. As we will see shortly, we also need a private helper function, , to make it easier to connect a newlyaddChild created directory to its parent. This leads to the following class definition: C++ namespace Filesystem { class DirectoryI : virtual public Directory, virtual public NodeI { public: virtual NodeSeq list(const Ice::Current&) const; DirectoryI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); void addChild(NodePrx child); private: NodeSeq _contents; }; } Servant Header File ExampleIce 3.4.2 Documentation 282 Copyright © 2011, ZeroC, Inc. Putting all this together, we end up with a servant header file, , as follows:FilesystemI.h C++ #include #include namespace Filesystem { class DirectoryI; typedef IceUtil::Handle DirectoryIPtr; class NodeI : virtual public Node { public: virtual std::string name(const Ice::Current&); NodeI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); void activate(const Ice::ObjectAdapterPtr&); private: std::string _name; Ice::Identity _id; DirectoryIPtr _parent; NodeI(const NodeI&); // Copy forbidden void operator=(const NodeI&); // Assignment forbidden }; typedef IceUtil::Handle NodeIPtr; class FileI : virtual public File, virtual public NodeI { public: virtual Lines read(const Ice::Current&); virtual void write(const Lines&, const Ice::Current& = Ice::Current()); FileI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); private: Lines _lines; }; typedef IceUtil::Handle FileIPtr; class DirectoryI : virtual public Directory, virtual public NodeI { public: virtual NodeSeq list(const Ice::Current&); DirectoryI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&); void addChild(const Filesystem::NodePrx&); private: Filesystem::NodeSeq _contents; }; } The Servant Implementation in C++ The implementation of our servants is mostly trivial, following from the class definitions in our header file.FilesystemI.h Implementing FileI The implementation of the and operations for files is trivial: we simply store the passed file contents in the dataread write _lines member. The constructor is equally trivial, simply passing its arguments through to the base class constructor:NodeIIce 3.4.2 Documentation 283 Copyright © 2011, ZeroC, Inc. C++ Filesystem::Lines Filesystem::FileI::read(const Ice::Current&) { return _lines; } void Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&) { _lines = text; } Filesystem::FileI::FileI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : NodeI(communicator, name, parent) { } Implementing DirectoryI The implementation of is equally trivial: the operation simply returns the data member and the constructorDirectoryI list _contents passes its arguments through to the base class constructor:NodeI C++ Filesystem::NodeSeq Filesystem::DirectoryI::list(const Ice::Current&) { return _contents; } Filesystem::DirectoryI::DirectoryI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : NodeI(name, parent) { } void Filesystem::DirectoryI::addChild(const NodePrx child) { _contents.push_back(child); } The only noteworthy thing is the implementation of : when a new directory or file is created, the constructor of the baseaddChild NodeI class calls on its own parent, passing it the proxy to the newly-created child. The implementation of appends theaddChild addChild passed reference to the contents list of the directory it is invoked on (which is the parent directory). Implementing NodeI The name operation of our class is again trivial: it simply returns the data member:NodeI _nameIce 3.4.2 Documentation 284 Copyright © 2011, ZeroC, Inc. C++ std::string Filesystem::NodeI::name(const Ice::Current&) { return _name; } The constructor creates an identity for the servant:NodeI C++ Filesystem::NodeI::NodeI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : _name(name), _parent(parent) { _id.name = parent ? IceUtil::generateUUID() : "RootDir"; } For the root directory, we use the fixed identity . This allows the to create a proxy for the root directory. For directories"RootDir" client other than the root directory, we use a .UUID as the identity Finally, provides the member function that adds the servant to the ASM and connects the child node to its parentNodeI activate directory: C++ void Filesystem::NodeI::activate(const Ice::ObjectAdapterPtr& a) { NodePrx thisNode = NodePrx::uncheckedCast(a?>add(this, _id)); if(_parent) { _parent?>addChild(thisNode); } } This completes our servant implementation. The complete source code is shown here once more: C++ #include #include using namespace std; // Slice Node::name() operation std::string Filesystem::NodeI::name(const Ice::Current&) { return _name; } // NodeI constructor Filesystem::NodeI::NodeI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : _name(name), _parent(parent)Ice 3.4.2 Documentation 285 Copyright © 2011, ZeroC, Inc. { // Create an identity. The root directory has the fixed identity "RootDir" // _id.name = parent ? IceUtil::generateUUID() : "RootDir"; } // NodeI activate() member function void Filesystem::NodeI::activate(const Ice::ObjectAdapterPtr& a) { NodePrx thisNode = NodePrx::uncheckedCast(a->add(this, _id)); if(_parent) { _parent->addChild(thisNode); } } // Slice File::read() operation Filesystem::Lines Filesystem::FileI::read(const Ice::Current&) { return _lines; } // Slice File::write() operation void Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&) { _lines = text; } // FileI constructor Filesystem::FileI::FileI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : NodeI(communicator, name, parent) { } // Slice Directory::list() operation Filesystem::NodeSeq Filesystem::DirectoryI::list(const Ice::Current& c) { return _contents; } // DirectoryI constructor Filesystem::DirectoryI::DirectoryI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : NodeI(communicator, name, parent) { } // addChild is called by the child in order to add // itself to the _contents member of the parent void Filesystem::DirectoryI::addChild(const NodePrx& child) {Ice 3.4.2 Documentation 286 Copyright © 2011, ZeroC, Inc. _contents.push_back(child); } See Also Slice for a Simple File System Example of a File System Client in C++ The ClassIce::Application C++ Mapping for Sequences slice2cpp Command-Line Options UUIDs as Identities in C++ Threads and Concurrency with C++Ice 3.4.2 Documentation 287 Copyright © 2011, ZeroC, Inc. The C++ Utility Library Ice for C++ includes a number of utility classes and functions in the namespace, which we summarize here for your reference.IceUtil Many of the classes and functions in are documented elsewhere in this manual so, where appropriate, the sections here simplyIceUtil reference the relevant pages. Topics The C++ AbstractMutex Class The C++ Cache Template The C++ Exception Class The C++ generateUUID Function The C++ Handle Template The C++ Handle Template Adaptors The C++ ScopedArray Template The C++ Shared and SimpleShared Classes The C++ Time Class The C++ Timer and TimerTask Classes Unicode and UTF-8 Conversion Functions in C++ Version Information in C++Ice 3.4.2 Documentation 288 Copyright © 2011, ZeroC, Inc. The C++ AbstractMutex Class AbstractMutex defines a mutex base interface used by the Freeze . The interface allows the evictor tobackground save evictor synchronize with servants that are stored in a Freeze database. The class has the following definition: C++ class AbstractMutex { public: typedef LockT Lock; typedef TryLockT TryLock; virtual ~AbstractMutex(); virtual void lock() const = 0; virtual void unlock() const = 0; virtual bool tryLock() const = 0; }; This class definition is provided in . The same header file also defines a few template implementation classesIceUtil/AbstractMutex.h that specialize , as described below.AbstractMutex AbstractMutexI This template class implements by forwarding all member functions to its template argument:AbstractMutex C++ template class AbstractMutexI : public AbstractMutex, public T { public: typedef LockT Lock; typedef TryLockT TryLock; virtual void lock() const { T::lock(); } virtual void unlock() const { T::unlock(); } virtual bool tryLock() const { return T::tryLock(); } virtual ~AbstractMutexI() {} }; AbstractMutexReadI This template class implements a read lock by forwarding the and functions to the and functionslock tryLock readLock tryReadLock of its template argument:Ice 3.4.2 Documentation 289 Copyright © 2011, ZeroC, Inc. C++ template class AbstractMutexReadI : public AbstractMutex, public T { public: typedef LockT Lock; typedef TryLockT TryLock; virtual void lock() const { T::readLock(); } virtual void unlock() const { T::unlock(); } virtual bool tryLock() const { return T::tryReadLock(); } virtual ~AbstractMutexReadI() {} }; AbstractMutexWriteI This template class implements a write lock by forwarding the and functions to the and lock tryLock writeLock tryWriteLock functions of its template argument: C++ template class AbstractMutexWriteI : public AbstractMutex, public T { public: typedef LockT Lock; typedef TryLockT TryLock; virtual void lock() const { T::writeLock(); } virtual void unlock() const { T::unlock(); } virtual bool tryLock() const { return T::tryWriteLock(); } virtual ~AbstractMutexWriteI() {} }; Apart from use with Freeze servants, these templates are also useful if, for example, you want to implement your own evictor. See Also Background Save EvictorIce 3.4.2 Documentation 290 Copyright © 2011, ZeroC, Inc. The C++ Cache Template This class allows you to efficiently maintain a cache that is backed by secondary storage, such as a Berkeley DB database, without holding a lock on the entire cache while values are being loaded from the database. If you want to create for servants that store their state in aevictors database, the class can simplify your evictor implementation considerably.Cache You may also want to examine the implementation of the in the source distribution; it usesFreeze background save evictor for its implementation.IceUtil::Cache The class has the following interface:Cache C++ template class Cache { public: typedef typename std::map::iterator Position; bool pin(const Key& k, const Handle& v); Handle pin(const Key& k); void unpin(Position p); Handle putIfAbsent(const Key& k, const Handle& v); Handle getIfPinned(const Key&, bool = false) const; void clear(); size_t size() const; protected: virtual Handle load(const Key& k) = 0; virtual void pinned(const Handle& v, Position p); virtual ~Cache(); }; Note that is an abstract base class — you must derive a concrete implementation from and provide an implementation of the Cache Cache and, optionally, of the member function.load pinned Internally, a maintains a map of name-value pairs. The key and value type of the map are supplied by the and templateCache Key Value arguments, respectively. The implementation of takes care of maintaining the map; in particular, it ensures that concurrent lookups byCache callers are possible without blocking even if some of the callers are currently loading values from the backing store. In turn, this is useful for evictor implementations, such as the Freeze . The class does not limit the number of entries in the cache —background save evictor Cache it is the job of the evictor implementation to limit the map size by calling on elements of the map that it wants to evict.unpin Your concrete implementation class must implement the function, whose job it is to load the value for the key from the backing storeload k and to return a handle to that value. Note that returns a value of type , that is, the value must be heap-allocatedload IceUtil::Handle and support the usual reference-counting functions for smart pointers. (The easiest way to achieve this is to derive the value from .)IceUtil::Shared If cannot locate a record for the given key because no such record exists, it must return a null handle. If fails for some otherload load reason, it can throw an exception, which is propagated back to the application code. Your concrete implementation class typically will also override the function (unless you want to have a cache that does not limit thepinned number of entries; the provided default implementation of is a no-op). The implementation calls whenever it haspinned Cache pinned added a value to the map as a result of a call to ; the function is therefore a callback that allows the derived class to find outpin pinned when a value has been added to the cache and informs the derived class of the value and its position in the cache. The parameter is a into the cache's internal map that records the position of the corresponding map entry.Position std::iterator (Note that the element type of map is opaque, so you should not rely on knowledge of the cache's internal key and value types.) Your implementation of must remember the position of the entry because that position is necessary to remove the corresponding entrypinned from the cache again.Ice 3.4.2 Documentation 291 Copyright © 2011, ZeroC, Inc. The public member functions of behave as follows:Cache bool pin(const Key& k, const Handle& v); To add a key-value pair to the cache, your evictor can call . The return value is true if the key and value were added; a false return valuepin indicates that the map already contained an entry with the given key and the original value for that key is unchanged. pin calls if it adds an entry.pinned This version of does call to retrieve the entry from backing store if it is not yet in the cache. This is useful when you add apin not load newly-created object to the cache. Once an entry is in the cache, it is guaranteed to remain in the cache at the same position in memory, and without its value being overwritten by another thread, until that entry is unpinned by a call to .unpin Handle pin(const Key& k); A second version of looks for the entry with the given key in the cache. If the entry is already in the cache, returns the entry's value.pin pin If no entry with the given key is in the cache, calls to retrieve the corresponding entry. If returns an entry, adds it to thepin load load pin cache and returns the entry's value. If the entry cannot be retrieved from the backing store, returns null.pin pin calls if it adds an entry.pinned The function is thread-safe, that is, it calls only once all other threads have unpinned the entry.load Once an entry is in the cache, it is guaranteed to remain in the cache at the same position in memory, and without its value being overwritten by another thread, until that entry is unpinned by a call to .unpin Handle putIfAbsent(const Key& k, const Handle& v); This function adds a key-value pair to the cache and returns a smart pointer to the value. If the map already contains an entry with the given key, that entry's value remains unchanged and returns its value. If no entry with the given key is in the cache, putIfAbsent putIfAbsent calls to retrieve the corresponding entry. If returns an entry, adds it to the cache and returns the entry's value. Ifload load putIfAbsent the entry cannot be retrieved from the backing store, returns null.putIfAbsent putIfAbsent calls if it adds an entry.pinned The function is thread-safe, that is, it calls only once all other threads have unpinned the entry.load Once an entry is in the cache, it is guaranteed to remain in the cache at the same position in memory, and without its value being overwritten by another thread, until that entry is unpinned by a call to .unpin Handle getIfPinned(const Key& k, bool wait = false) const; This function returns the value stored for the key .k If an entry for the given key is in the map, the function returns the value immediately, regardless of the value of .wait If no entry for the given key is in the map and the parameter is false, the function returns a null handle.wait If no entry for the given key is in the map and the parameter is true, the function blocks the calling thread if another thread iswait currently attempting to load the same entry; once the other thread completes, completes and returns the valuegetIfPinned added by the other thread. void unpin(Position p); This function removes an entry from the map. The iterator determines which entry to remove. (It must be an iterator that previously wasp passed to .) The iterator is invalidated by this operation, so you must not use it again once returns. (Note that the pinned p unpin Cache implementation ensures that updates to the map never invalidate iterators to existing entries in the map; invalidates only the iteratorunpin for the removed entry.) void clear(); This function removes all entries in the map. size_t size() const; This function returns the number of entries in the map. See AlsoIce 3.4.2 Documentation 292 Copyright © 2011, ZeroC, Inc. Servant Evictors The C++ Handle Template The C++ Shared and SimpleShared Classes Background Save EvictorIce 3.4.2 Documentation 293 Copyright © 2011, ZeroC, Inc. The C++ Exception Class This class is at the root of the derivation tree for and encapsulates functionality that is common to all and Ice exceptions Ice IceUtil exceptions: C++ class Exception : public std::exception { public: Exception(); Exception(const char* file, int line); virtual ~Exception() throw(); virtual std::string ice_name() const; virtual void ice_print(std::ostream&) const; virtual const char* what() const throw(); virtual Exception* ice_clone() const; virtual void ice_throw() const; const char* ice_file() const; int ice_line() const; }; The second constructor stores a file name and line number in the exception that are returned by the and memberice_file ice_line functions, respectively. This allows you to identify the source of an exception by passing the and preprocessor macros__FILE__ __LINE__ to the constructor. The member function is a synonym for . The default implementation of prints the file name, line number, andwhat ice_print ice_print the name of the exception. The remaining member functions are described in the .C++ Mapping for Exceptions See Also C++ Mapping for ExceptionsIce 3.4.2 Documentation 294 Copyright © 2011, ZeroC, Inc. The C++ generateUUID Function Universally-unique identifiers (UUIDs) are often used in the of Ice objects. The C++ standard does not include a function foridentities generating UUIDs, therefore Ice provides the function for use in portable applications. The signature of IceUtil::generateUUID is:generateUUID C++ std::string generateUUID(); The function returns a string like the following: 02b066f5-c762-431c-8dd3-9b1941355e41 Each invocation returns a new identifier that differs from all previous ones. See Also Object IdentityIce 3.4.2 Documentation 295 Copyright © 2011, ZeroC, Inc. The C++ Handle Template IceUtil::Handle implements a smart reference-counted pointer type. are used to guarantee automatic deletion ofSmart pointers heap-allocated class instances. Handle is a template class with the following interface:Ice 3.4.2 Documentation 296 Copyright © 2011, ZeroC, Inc. C++ template class Handle : /* ... */ { public: typedef T element_type; T* _ptr; T* operator->() const; T& operator*() const; T* get() const; operator bool() const; void swap(HandleBase& other); Handle(T* p = 0); template Handle(const Handle& r); Handle(const Handle& r); ~Handle(); Handle& operator=(T* p); template Handle& operator=(const Handle& r); Handle& operator=(const Handle& r); template static Handle dynamicCast(const HandleBase& r); template static Handle dynamicCast(Y* p); }; template bool operator==(const Handle& lhs, const Handle& rhs); template bool operator!=(const Handle& lhs, const Handle& rhs); template bool operator<(const Handle& lhs, const Handle& rhs); template bool operator<=(const Handle& lhs, const Handle& rhs); template bool operator>(const Handle& lhs, const Handle& rhs); template bool operator>=(const Handle& lhs, const Handle& rhs); Note that the actual implementation is split into a base and a derived class. For simplicity, we show the combined interface here. If you want to see the full implementation detail, it can be found in .IceUtil/Handle.h The template argument must be a class that derives from or (or that implements reference counting with the sameShared SimpleSharedIce 3.4.2 Documentation 297 Copyright © 2011, ZeroC, Inc. interface as these classes). This is quite a large interface, but all it really does is to faithfully mimic the behavior of ordinary C++ class instance pointers. Rather than discussing each member function in detail, we provide a simple overview here that outlines the most important points. Please see the discussion of for more examples of using smart pointers.Ice objects element_type This type definition follows the STL convention of defining the element type with the fixed name so you can use it forelement_type template programming or the definition of generic containers. _ptr This data member stores the pointer to the underlying heap-allocated class instance. Constructors, copy constructor, and assignment operators These member functions allow you to construct, copy, and assign smart pointers as if they were ordinary pointers. In particular, the constructor and assignment operator are overloaded to work with raw C++ class instance pointers, which results in the "adoption" of the raw pointer by the smart pointer. For example, the following code works correctly and does not cause a memory leak: C++ typedef Handle MyClassPtr; void foo(const MyClassPtr&); // ... foo(new MyClass); // OK, no leak here. , , and operator-> operator* get The arrow and indirection operators allow you to apply the usual pointer syntax to smart pointers to use the target of a smart pointer. The member function returns the class instance pointer to the underlying reference-counted class instance; the return value is the value of get ._ptr dynamicCast This member function works exactly like a C++ : it tests whether the argument supports the specified type and, if so, returnsdynamic_cast a non-null pointer; if the target does not support the specified type, it returns null. The reason for not using an actual and using a function instead is that dynamic_cast dynamicCast dynamic_cast only operates on pointer types, but is a class.IceUtil::Handle For example: C++ MyClassPtr p = ...; MyOtherClassPtr o = ...; o = MyOtherClassPtr::dynamicCast(p); if (o) { // o points at an instance of type MyOtherClass. } else { // p points at something that is // not compatible with MyOtherClass. } Note that this example also illustrates the use of : when used in a boolean context, a smart pointer returns true if it isoperator boolIce 3.4.2 Documentation 298 Copyright © 2011, ZeroC, Inc. non-null and false otherwise. Comparison operators: ==, !=, <, <=, >, >= The comparison operators compare the value of the underlying class instance pointer, that is, they compare the value returned by . Inget other words, returns true if two smart pointers point at the same underlying class instance, and the ordering operators compare the== memory addresses of the underlying class instances. See Also Smart Pointers for Classes The C++ Shared and SimpleShared ClassesIce 3.4.2 Documentation 299 Copyright © 2011, ZeroC, Inc. The C++ Handle Template Adaptors IceUtil provides adaptors that support use of with STL algorithms. Each template function returns a corresponding functionsmart pointers object that is for use by an STL algorithm. The adaptors are defined in the header .IceUtil/Functional.h Here is a list of the adaptors: memFun memFun1 voidMemFun voidMemFun1 secondMemFun secondMemFun1 secondVoidMemFun secondVoidMemFun1 constMemFun constMemFun1 constVoidMemFun constVoidMemFun1 secondConstMemFun secondConstMemFun1 secondConstVoidMemFun secondConstVoidMemFun1 As you can see, the adaptors are in two groups. The first group operates on non-const smart pointers, whereas the second group operates on smart pointers (for example, on smart pointers declared as ).const const MyClassPtr Each group is further divided into two sub-groups. The adaptors in the first group operate on the target of a smart pointer, whereas the adapters operate on the second element of a pair, where that element is a smart pointer.second Each of the four sub-groups contains four adaptors: memFun This adaptor is used for member functions that return a value and do not accept an argument. For example: C++ class MyClass : public IceUtil::Shared { public: MyClass(int i) : _i(i) {} int getVal() { return _i; } private: int _i; }; typedef IceUtil::Handle MyClassPtr; // ... vector mcp; mcp.push_back(new MyClass(42)); mcp.push_back(new MyClass(99)); transform(mcp.begin(), mcp.end(), ostream_iterator(cout, " "), IceUtil::memFun(&MyClass::getVal)); cout << endl; This code invokes the member function on each instance that is pointed at by smart pointers in the vector and prints the returngetVal mcpIce 3.4.2 Documentation 300 Copyright © 2011, ZeroC, Inc. value of on , separated by spaces. The output from this code is:getVal cout 42 99 memFun1 This adaptor is used for member functions that return a value and accept a single argument. For example: C++ class MyClass : public IceUtil::Shared { public: MyClass(int i) : _i(i) {} int plus(int v) { return _i + v; } private: int _i; }; typedef IceUtil::Handle MyClassPtr; // ... vector mcp; mcp.push_back(new MyClass(2)); mcp.push_back(new MyClass(4)); mcp.push_back(new MyClass(6)); int A[3] = { 5, 7, 9 }; transform(mcp.begin(), mcp.end(), A, ostream_iterator(cout, " "), IceUtil::memFun1(&MyClass::plus)); cout << endl; This code invokes the member function on each instance that is pointed at by smart pointers in the vector and prints the returnplus mcp value of a call to on , separated by spaces. The calls to are successively passed the values stored in the array . Theplus cout plus A output from this code is: 7 11 15 voidMemFun This adaptor is used for member functions that do not return a value and do not accept an argument. For example:Ice 3.4.2 Documentation 301 Copyright © 2011, ZeroC, Inc. C++ class MyClass : public IceUtil::Shared { public: MyClass(int i) : _i(i) {} void print() { cout << _i << endl; } private: int _i; }; typedef IceUtil::Handle MyClassPtr; // ... vector mcp; mcp.push_back(new MyClass(2)); mcp.push_back(new MyClass(4)); mcp.push_back(new MyClass(6)); for_each(mcp.begin(), mcp.end(), IceUtil::voidMemFun(&MyClass::print)); This code invokes the member function on each instance that is pointed at by smart pointers in the vector . The output from thisprint mcp code is: 2 4 6 voidMemFun1 This adaptor is used for member functions that do not return a value and accept a single argument. For example: C++ class MyClass : public IceUtil::Shared { public: MyClass(int i) : _i(i) {} void printPlus(int v) { cout << _i + v << endl; } private: int _i; }; typedef IceUtil::Handle MyClassPtr; vector mcp; mcp.push_back(new MyClass(2)); mcp.push_back(new MyClass(4)); mcp.push_back(new MyClass(6)); for_each( mcp.begin(), mcp.end(), bind2nd(IceUtil::voidMemFun1(&MyClass::printPlus), 3)); This code invokes the member function on each instance that is pointed at by smart pointers in the vector . The output fromprintPlus mcp this code is: 5 7 9 As mentioned earlier, the versions of the adaptors operate on the second element of a , where second std::pair T2Ice 3.4.2 Documentation 302 Copyright © 2011, ZeroC, Inc. must be a smart pointer. Most commonly, these adaptors are used to apply an algorithm to each lookup value of a map or multi-map. Here is an example: C++ class MyClass : public IceUtil::Shared { public: MyClass(int i) : _i(i) {} int plus(int v) { return _i + v; } private: int _i; }; typedef IceUtil::Handle MyClassPtr; // ... map m; m["two"] = new MyClass(2); m["four"] = new MyClass(4); m["six"] = new MyClass(6); int A[3] = { 5, 7, 9 }; transform( m.begin(), m.end(), A, ostream_iterator(cout, " "), IceUtil::secondMemFun1(&MyClass::plus)); This code invokes the member function on the class instance denoted by the smart pointer member of each pair in theplus second dictionary . The output from this code is:m 9 13 11 Note that is a template that requires three arguments: the return type of the member function to be invoked, the key type ofsecondMemFun1 the dictionary, and the type of the class that is pointed at by the smart pointer. In general, the adaptors require the following template arguments:second C++ secondMemFun secondMemFun1 secondVoidMemFun secondVoidMemFun where is the return type of the member function, is the type of the first member of the pair, and is the class that contains the memberR K T function. See Also The C++ Handle TemplateIce 3.4.2 Documentation 303 Copyright © 2011, ZeroC, Inc. The C++ ScopedArray Template IceUtil::ScopedArray is a smart pointer class similar to . However, instead of managing the memory for class instances, Handle manages memory for an array. This class is provided mainly for use with the . However, you can use it with arraysScopedArray stream API for other purposes. Here is the definition of the template in full: C++ template class ScopedArray : private IceUtil::noncopyable { public: explicit ScopedArray(T* ptr = 0) : _ptr(ptr) { } ScopedArray(const ScopedArray& other) { _ptr = other._ptr; const_cast(other)._ptr = 0; } ~ScopedArray() { if (_ptr != 0) delete[] _ptr; } void reset(T* ptr = 0) { assert(ptr == 0 || ptr != _ptr); if (_ptr != 0) delete[] _ptr; _ptr = ptr; } T& operator[](size_t i) const { assert(_ptr != 0); assert(i >= 0); return _ptr[i]; } T* get() const { return _ptr; } void swap(ScopedArray& a) { T* tmp = a._ptr; a._ptr = _ptr; _ptr = tmp; } private: T* _ptr; }; The class allows you to allocate an array on the heap and assign its pointer to a instance. When the instance goes out ofScopedArray scope, it calls on the array, so you do not need to deallocate the array explicitly yourself. This greatly reduces the risk of adelete[] memory leak due to an early return or uncaught exception. See Also C++ Streaming InterfacesIce 3.4.2 Documentation 304 Copyright © 2011, ZeroC, Inc. The C++ Shared and SimpleShared Classes IceUtil::Shared and are base classes that implement the reference-counting mechanism for IceUtil::SimpleShared smart pointers . The two classes provide identical interfaces; the difference between and is that is not thread-safeShared SimpleShared SimpleShared and, therefore, can only be used if the corresponding class instances are accessed only by a single thread. ( is marginallySimpleShared faster than because it avoids the locking overhead that is incurred by .)Shared Shared The interface of looks as follows. (Because has the same interface, we do not show it separately here.)Shared SimpleShared C++ class Shared { public: Shared(); Shared(const Shared&); virtual ~Shared(); Shared& operator=(const Shared&); virtual void __incRef(); virtual void __decRef(); virtual int __getRef() const; virtual void __setNoDelete(bool); }; The class maintains a reference that is initialized to zero by the constructor. increments the reference count and __incRef __decRef decrements it. If, during a call to , after decrementing the reference count, the reference count drops to zero, calls __decRef __decRef , which causes the corresponding class instance to delete itself. The copy constructor increments the reference count of thedelete this copied instance, and the assignment operator increments the reference count of the source and decrements the reference count of the target. The member function returns the value of the reference count and is useful mainly for debugging.__getRef The member function can be used to temporarily disable self-deletion and re-enable it again. This provides __setNoDelete exception when you initialize a smart pointer with the pointer of a class instance during construction.safety this To create a class that is reference-counted, you simply derive the class from and define a smart pointer type for the class, forShared example: C++ class MyClass : public IceUtil::Shared { // ... }; typedef IceUtil::Handle MyClassPtr; See Also The C++ Handle Template Smart Pointers for ClassesIce 3.4.2 Documentation 305 Copyright © 2011, ZeroC, Inc. The C++ Time Class The class provides basic facilities for getting the current time, constructing time intervals, adding and subtracting times, and comparingTime times: C++Ice 3.4.2 Documentation 306 Copyright © 2011, ZeroC, Inc. namespace IceUtil { typedef ... Int64; class Time { public: enum Clock { Realtime, Monotonic }; Time(Clock = Realtime); static Time now(); static Time seconds(Int64); static Time milliSeconds(Int64); static Time microSeconds(Int64); Int64 toSeconds() const; Int64 toMilliSeconds() const; Int64 toMicroSeconds() const; double toSecondsDouble() const; double toMilliSecondsDouble() const; double toMicroSecondsDouble() const; std::string toDateTime() const; std::string toDuration() const; Time operator-() const; Time operator-(const Time&) const; Time operator+(const Time&) const; Time operator*(int) const; Time operator*(Int64) const; Time operator*(double) const; double operator/(const Time&) const; Time operator/(int) const; Time operator/(Int64) const; Time operator/(double) const; Time& operator-=(const Time&); Time& operator+=(const Time&); Time& operator*=(int); Time& operator*=(Int64); Time& operator*=(double); Time& operator/=(int); Time& operator/=(Int64); Time& operator/=(double); bool operator<(const Time&) const; bool operator<=(const Time&) const; bool operator>(const Time&) const; bool operator>=(const Time&) const; bool operator==(const Time&) const; bool operator!=(const Time&) const; #ifndef _WIN32 operator timeval() const; #endif }; std::ostream& operator<<(std::ostream&, const Time&); } The member functions behave as follows:Ice 3.4.2 Documentation 307 Copyright © 2011, ZeroC, Inc. Time Internally, the class stores ticks in microsecond units. For absolute time, this is the number of microseconds since the Unix epochTime (00:00:00 UTC on 1 Jan. 1970). For durations, this is the number of microseconds in the duration. The default constructor initializes the tick count to zero and selects the real-time clock. Constructing with an argument of selects the monotonic clock on platformsTime Monotonic that support it; the real-time clock is used on other platforms. now This function constructs a object that is initialized to the current time of day.Time seconds, milliSeconds, microSeconds These functions construct objects from the argument in the specified units. For example, the following statement creates a timeTime duration of one minute: C++ IceUtil::Time t = IceUtil::Time::seconds(60); toSeconds, toMilliSeconds, toMicroSeconds The member functions provide explicit conversion of a duration to seconds, milliseconds, and microseconds, respectively. The return value is a 64-bit signed integer ( ). For example:IceUtil::Int64 C++ IceUtil::Time t = IceUtil::Time::milliSeconds(2000); IceUtil::Int64 secs = t.toSeconds(); // Returns 2 toSecondsDouble, toMilliSecondsDouble, toMicroSecondsDouble The member functions provide explicit conversion of a duration to seconds, milliseconds, and microseconds, respectively. The return value is of type .double toDateTime This function returns a human-readable representation of a value as a date and time.Time toDuration This function returns a human-readable representation of a value as a duration.Time Operators Time provides operators that allow you to add, subtract, multiply, and divide times. For example: C++ IceUtil::Time oneMinute = IceUtil::Time::seconds(60); IceUtil::Time oneMinuteAgo = IceUtil::Time::now() - oneMinute; The multiplication and division operators permit you to multiply and divide a duration. Note that these operators provide overloads for , int , and .long long double The comparison operators allow you to compare times and time intervals with each other, for example: IceUtil::Time oneMinute = IceUtil::Time::seconds(60); IceUtil::Time twoMinutes = IceUtil::Time::seconds(120); assert(oneMinute < twoMinutes); The operator converts a object to a , defined as follows:timeval Time struct timevalIce 3.4.2 Documentation 308 Copyright © 2011, ZeroC, Inc. C++ struct timeval { long tv_sec; long tv_usec; }; The conversion is useful for API calls that require a argument, such as . To convert a duration into a struct timeval select timeval structure, simply assign a object to a :Time struct timeval C++ IceUtil::Time oneMinute = IceUtil::Time::seconds(60); struct timeval tv; tv = t; Note that this member function is not available under Windows. std::ostream& operator<<(std::ostream&, Time&); This operator prints the number of whole seconds since the epoch. See Also The C++ Timer and TimerTask ClassesIce 3.4.2 Documentation 309 Copyright © 2011, ZeroC, Inc. The C++ Timer and TimerTask Classes The class allows you to schedule some code for once-only or repeated execution after some time interval elapses. The code to beTimer executed resides in a class you derive from :TimerTask C++ class Timer; typedef IceUtil::Handle TimerPtr; class TimerTask : virtual public IceUtil::Shared { public: virtual ~TimerTask() { } virtual void runTimerTask() = 0; }; typedef IceUtil::Handle TimerTaskPtr; Your derived class must override the member function; the code in this method is executed by the timer. If the code yourunTimerTask want to run requires access to some program state, you can pass that state into the constructor of your class or, alternatively, set that state via member functions of your class before scheduling it with a timer. The class invokes the member function to run your code. The class has the following definition:Timer runTimerTask C++ class Timer : /* ... */ { public: Timer(); Timer(int priority); void schedule(const TimerTaskPtr& task, const IceUtil::Time& interval); void scheduleRepeated(const TimerTaskPtr& task, const IceUtil::Time& interval); bool cancel(const TimerTaskPtr& task); void destroy(); }; typedef IceUtil::Handle TimerPtr; Intervals are specified using objects.Time The constructor is overloaded to allow you specify a . The priority controls the priority of the thread that executes your task.thread priority The member function schedules an instance of your timer task for once-only execution after the specified time interval hasschedule elapsed. Your code is executed by a separate thread that is created by the class. The function throws an Timer if you invoke it on a destroyed timer.IllegalArgumentException The member function runs your task repeatedly, at the specified time interval. Your code is executed by a separatescheduleRepeated thread that is created by the class; the same thread is used every time your code runs. The function throws an Timer if you invoke it on a destroyed timer.IllegalArgumentException If your code throws an exception, the class ignores the exception, that is, for a task that is scheduled to run repeatedly, an exceptionTimer in the current execution does not cancel the next execution. If your code takes longer to execute than the time interval you have specified for repeated execution, the second execution is delayed accordingly. For example, if you ask for repeated execution once every five seconds, and your code takes ten seconds to complete, then the second execution of your task starts five seconds after the previous execution finishes, that is, the interval specifies the wait time between successive executions. A instance that has already been scheduled with a instance cannot be scheduled again with the same instanceTimerTask Timer Timer until the task has completed or been canceled.Ice 3.4.2 Documentation 310 Copyright © 2011, ZeroC, Inc. For a single instance, the execution of all registered tasks is serialized. The wait interval applies on a per-task basis so, if youTimer schedule task A at an interval of five seconds, and task B at an interval of ten seconds, successive runs of task A start no sooner than five seconds after the previous task A has finished, and successive runs of task B start no sooner than ten seconds after the previous task B has finished. If, at the time a task is scheduled to run, another task is still running, the new task's execution is delayed until the previous task has finished. If you want scheduled tasks to run concurrently, you can create several instances; tasks then execute in as many threadsTimer concurrently as there are instances.Timer The member function removes a task from a timer's schedule. In other words, it stops a task that is scheduled for repeatedcancel execution from being executed again. (For once-only tasks, does nothing.) If you cancel a task while it is executing, returnscancel cancel immediately and the currently running task is allowed to complete normally; that is, does not wait for any currently running task tocancel complete. The return value is true if removed the task from the schedule. This is the case if you invoke on a task that is scheduled forcancel cancel repeated execution and this was the first time you cancelled that task; subsequent calls to return false. Calling on a taskcancel cancel scheduled for once-only execution always returns false, as does calling on a destroyed timer.cancel The member function removes all tasks from the timer's schedule. If you call from any thread other than the timer's owndestroy destroy execution thread, it joins with the currently executing task (if any), so the function does not return until the current task has completed. If you call from the timer's own execution thread, it instead detaches the timer's execution thread. Calling a second time on thedestroy destroy same instance has no effect. Similarly, calling on a destroyed timer has no effect.Timer cancel Note that you must call on a instance before allowing it to go out of scope; failing to do so causes undefined behavior.destroy Timer Calls to or on a destroyed timer do nothing.schedule scheduleRepeated See Also The C++ Time Class The C++ Thread ClassesIce 3.4.2 Documentation 311 Copyright © 2011, ZeroC, Inc. Unicode and UTF-8 Conversion Functions in C++ The namespace contains two helper functions that allow you to convert between wide strings containing Unicode charactersIceUtil (either 16- or 32-bit, depending on your native size) and narrow strings in UTF-8 encoding:wchar_t C++ enum ConversionFlags { strictConversion, lenientConversion }; std::string wstringToString(const std::wstring&, ConversionFlags = lenientConversion); std::wstring stringToWstring(const std::string&, ConversionFlags = lenientConversion); These functions always convert to and from UTF-8 encoding, that is, they ignore any locale setting that might specify a different encoding. Byte sequences that are illegal, such as , result in a . For other errors, the 0xF4908080 UTFConversionException ConversionFlags parameter determines how rigorously the functions check for errors. When set to (the default), the functions toleratelenientConversion isolated surrogates and irregular sequences, and substitute the UTF-32 replacement character for character values above 0x0000FFFD . When set to , the functions do not tolerate such errors and throw a instead:0x10FFFF strictConversion UTFConversionException C++ enum ConversionError { partialCharacter, badEncoding }; class UTFConversionException : public Exception { public: UTFConversionException(const char* file, int line, ConversionError r); ConversionError conversionError() const; // ... }; The member function returns the reason for the failure:conversionError partialCharacter The UTF-8 source string contains a trailing incomplete UTF-8 byte sequence. badEncoding The UTF-8 source string contains a byte sequence that is not a valid UTF-8 encoded character, or the Unicode source string contains a bit pattern that does not represent a valid Unicode character.Ice 3.4.2 Documentation 312 Copyright © 2011, ZeroC, Inc. Version Information in C++ The header file defines two macros that expand to the version of the Ice run time:IceUtil/Config.h C++ #define ICE_STRING_VERSION "3.4.2" // ".." #define ICE_INT_VERSION 30402 // AABBCC, with AA=major, // BB=minor, CC=patch ICE_STRING_VERSION is a string literal in the form , for example, . For beta releases, the version is .. 3.4.2 , for example, .. b 3.4b INT_VERSION is an integer literal in the form , where is the major version number, is the minor version number, and is theAABBCC AA BB CC patch level, for example, for version 3.4.2. For beta releases, the patch level is set to 51 so, for example, for version 3.4b, the value is30402 .30451Ice 3.4.2 Documentation 313 Copyright © 2011, ZeroC, Inc. Java Mapping Topics Client-Side Slice-to-Java Mapping Server-Side Slice-to-Java Mapping The Java Utility LibraryIce 3.4.2 Documentation 314 Copyright © 2011, ZeroC, Inc. Client-Side Slice-to-Java Mapping In this section, we present the client-side Slice-to-Java mapping. The client-side Slice-to-Java mapping defines how Slice data types are translated to Java types, and how clients invoke operations, pass parameters, and handle errors. Much of the Java mapping is intuitive. For example, Slice sequences map to Java arrays, so there is essentially nothing new you have to learn in order to use Slice sequences in Java. The Java API to the Ice run time is fully thread-safe. Obviously, you must still synchronize access to data from different threads. For example, if you have two threads sharing a sequence, you cannot safely have one thread insert into the sequence while another thread is iterating over the sequence. However, you only need to concern yourself with concurrent access to your own data — the Ice run time itself is fully thread safe, and none of the Ice API calls require you to acquire or release a lock before you safely can make the call. Much of what appears in this chapter is reference material. We suggest that you skim the material on the initial reading and refer back to specific sections as needed. However, we recommend that you read at least the mappings for , , and inexceptions interfaces operations detail because these sections cover how to call operations from a client, pass parameters, and handle exceptions. In order to use the Java mapping, you should need no more than the Slice definition of your application and knowledge of the Java mapping rules. In particular, looking through the generated code in order to discern how to use the Java mapping is likely to be inefficient, due to the amount of detail. Of course, occasionally, you may want to refer to the generated code to confirm a detail of the mapping, but we recommend that you otherwise use the material presented here to see how to write your client-side code. The PackageIce All of the APIs for the Ice run time are nested in the package, to avoid clashes with definitions for other libraries orIce applications. Some of the contents of the package are generated from Slice definitions; other parts of the Ice Ice package provide special-purpose definitions that do not have a corresponding Slice definition. We will incrementally cover the contents of the package throughout the remainder of the book.Ice Topics Java Mapping for Identifiers Java Mapping for Modules Java Mapping for Built-In Types Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for Constants Java Mapping for Exceptions Java Mapping for Interfaces Java Mapping for Operations Java Mapping for Classes Serializable Objects in Java Customizing the Java Mapping Asynchronous Method Invocation (AMI) in Java Using the Slice Compiler for Java Using Slice Checksums in Java Example of a File System Client in JavaIce 3.4.2 Documentation 315 Copyright © 2011, ZeroC, Inc. Java Mapping for Identifiers A Slice maps to an identical Java identifier. For example, the Slice identifier becomes the Java identifier . There isidentifier Clock Clock one exception to this rule: if a Slice identifier is the same as a Java keyword or is an identifier reserved by the Ice run time (such as ), the corresponding Java identifier is prefixed with an underscore. For example, the Slice identifier is mapped as checkedCast while ._while You should try to as much as possible.avoid such identifiers A single Slice identifier often results in several Java identifiers. For example, for a Slice interface named , the generated Java code usesFoo the identifiers and (among others). If the interface has the name , the generated identifiers are and (Foo FooPrx while _while whilePrx ), that is, the underscore prefix is applied only to those generated identifiers that actually require it.not _whilePrx See Also Lexical Rules Java Mapping for Modules Java Mapping for Built-In Types Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for Constants Java Mapping for ExceptionsIce 3.4.2 Documentation 316 Copyright © 2011, ZeroC, Inc. Java Mapping for Modules A Slice maps to a Java package with the same name as the Slice module. The mapping preserves the nesting of the Slicemodule definitions. For example: Slice // Definitions at global scope here... module M1 { // Definitions for M1 here... module M2 { // Definitions for M2 here... }; }; // ... module M1 { // Reopen M1 // More definitions for M1 here... }; This definition maps to the corresponding Java definitions: Java package M1; // Definitions for M1 here... package M1.M2; // Definitions for M2 here... package M1; // Definitions for M1 here... Note that these definitions appear in the appropriate source files; source files for definitions in module are generated in directory M1 M1 underneath the top-level directory, and source files for definitions for module are generated in directory underneath the top-levelM2 M1/M2 directory. You can set the top-level output directory using the option with .--output-dir slice2java See Also Modules Using the Slice Compilers Java Mapping for Identifiers Java Mapping for Built-In Types Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for Constants Java Mapping for ExceptionsIce 3.4.2 Documentation 317 Copyright © 2011, ZeroC, Inc. Java Mapping for Built-In Types The Slice are mapped to Java types as follows:built-in types Slice Java bool boolean byte byte short short int int long long float float double double string String Mapping of Slice built-in types to Java. See Also Basic Types Java Mapping for Identifiers Java Mapping for Modules Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for Constants Java Mapping for ExceptionsIce 3.4.2 Documentation 318 Copyright © 2011, ZeroC, Inc. Java Mapping for Enumerations A Slice maps to the corresponding enumeration in Java. For example:enumeration Slice enum Fruit { Apple, Pear, Orange }; The Java mapping for is shown below:Fruit Java public enum Fruit implements java.io.Serializable { Apple, Pear, Orange; // ... } Given the above definitions, we can use enumerated values as follows: Java Fruit f1 = Fruit.Apple; Fruit f2 = Fruit.Orange; if (f1 == Fruit.Apple) // Compare with constant // ... if (f1 == f2) // Compare two enums // ... switch (f2) { // Switch on enum case Fruit.Apple: // ... break; case Fruit.Pear // ... break; case Fruit.Orange // ... break; } Note that the generated class contains a number of other members, which we have not shown. These members are internal to the Ice run time and you must not use them in your application code (because they may change from release to release). See Also Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for DictionariesIce 3.4.2 Documentation 319 Copyright © 2011, ZeroC, Inc. Java Mapping for Structures On this page: Basic Java Mapping for Structures Java Default Constructors for Structures Basic Java Mapping for Structures A Slice maps to a Java class with the same name. For each Slice data member, the Java class contains a corresponding publicstructure data member. For example, here is our structure once more:Employee Slice struct Employee { long number; string firstName; string lastName; }; The Slice-to-Java compiler generates the following definition for this structure: Java public final class Employee implements java.lang.Cloneable, java.io.Serializable { public long number; public String firstName; public String lastName; public Employee {} public Employee(long number, String firstName, String lastName) { this.number = number; this.firstName = firstName; this.lastName = lastName; } public boolean equals(java.lang.Object rhs) { // ... } public int hashCode() { // ... } public java.lang.Object clone() java.lang.Object o; try { o = super.clone(); } catch(java.lang.CloneNotSupportedException ex) { assert false; // impossible } return o; } } For each data member in the Slice definition, the Java class contains a corresponding public data member of the same name. Note that you can optionally for data members to use getters and setters instead.customize the mapping The member function compares two structures for equality. Note that the generated class also provides the usual and equals hashCodeIce 3.4.2 Documentation 320 Copyright © 2011, ZeroC, Inc. methods. ( has the default behavior of making a shallow copy.)clone clone Java Default Constructors for Structures Structures have a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize members of structure and enumerated types because the Ice run time does not accept null as a legal value for these types. If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition Structures also have a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assign to its members). See Also Structures Java Mapping for Enumerations Java Mapping for Sequences Java Mapping for Dictionaries Customizing the Java MappingIce 3.4.2 Documentation 321 Copyright © 2011, ZeroC, Inc. Java Mapping for Sequences A Slice maps to a Java array. This means that the Slice-to-Java compiler does not generate a separate named type for a Slicesequence sequence. For example: Slice sequence FruitPlatter; This definition simply corresponds to the Java type . Naturally, because Slice sequences are mapped to Java arrays, you can takeFruit[] advantage of all the array functionality provided by Java, such as initialization, assignment, cloning, and the member. For example:length Java Fruit[] platter = { Fruit.Apple, Fruit.Pear }; assert(platter.length == 2); Alternate mappings for sequence types are also possible. See Also Sequences Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Dictionaries Customizing the Java MappingIce 3.4.2 Documentation 322 Copyright © 2011, ZeroC, Inc. Java Mapping for Dictionaries Here is the definition of our once more:EmployeeMap Slice dictionary EmployeeMap; As for sequences, the Java mapping does not create a separate named type for this definition. Instead, the dictionary is simply an instance of the generic type , where is the mapping of the key type and is the mapping of the value type. In the examplejava.util.Map< , >KVK V above, is mapped to the Java type . The following code demonstrates how to allocateEmployeeMap java.util.Map and use an instance of :EmployeeMap Java java.util.Map em = new java.util.HashMap(); Employee e = new Employee(); e.number = 31; e.firstName = "James"; e.lastName = "Gosling"; em.put(e.number, e); The type-safe nature of the mapping makes iterating over the dictionary quite convenient: Java for (java.util.Map.Entry i : em.entrySet()) { long num = i.getKey(); Employee emp = i.getValue(); System.out.println(emp.firstName + " was employee #" + num); } Alternate mappings for dictionary types are also possible. See Also Dictionaries Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Customizing the Java MappingIce 3.4.2 Documentation 323 Copyright © 2011, ZeroC, Inc. Java Mapping for Constants Here are the sample once more:constant definitions Slice const bool AppendByDefault = true; const byte LowerNibble = 0x0f; const string Advice = "Don't Panic!"; const short TheAnswer = 42; const double PI = 3.1416; enum Fruit { Apple, Pear, Orange }; const Fruit FavoriteFruit = Pear; Here are the generated definitions for these constants: Java public interface AppendByDefault { boolean value = true; } public interface LowerNibble { byte value = 15; } public interface Advice { String value = "Don't Panic!"; } public interface TheAnswer { short value = 42; } public interface PI { double value = 3.1416; } public interface FavoriteFruit { Fruit value = Fruit.Pear; } As you can see, each Slice constant is mapped to a Java interface with the same name as the constant. The interface contains a member named that holds the value of the constant.value See Also Constants and Literals Java Mapping for Identifiers Java Mapping for Modules Java Mapping for Built-In Types Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for ExceptionsIce 3.4.2 Documentation 324 Copyright © 2011, ZeroC, Inc. Java Mapping for Exceptions On this page: Java Mapping for User Exceptions Java Default Constructors for User Exceptions Java Mapping for Run-Time Exceptions Java Mapping for User Exceptions Here is a fragment of the once more:Slice definition for our world time server Slice exception GenericError { string reason; }; exception BadTimeVal extends GenericError {}; exception BadZoneName extends GenericError {}; These exception definitions map as follows: Java public class GenericError extends Ice.UserException { public String reason; public GenericError() {} public GenericError(Throwable cause) { super(cause); } public GenericError(String reason) { this.reason = reason; } public GenericError(String reason, Throwable cause) { super(cause); this.reason = reason; } public String ice_name() { return "GenericError"; } } public class BadTimeVal extends GenericError { public BadTimeVal() {} public BadTimeVal(Throwable cause) { super(cause); } public BadTimeVal(String reason) {Ice 3.4.2 Documentation 325 Copyright © 2011, ZeroC, Inc. super(reason); } public BadTimeVal(String reason, Throwable cause) { super(reason, cause); } public String ice_name() { return "BadTimeVal"; } } public class BadZoneName extends GenericError { public BadZoneName() {} public BadZoneName(Throwable cause) { super(cause); } public BadZoneName(String reason) { super(reason); } public BadZoneName(String reason, Throwable cause) { super(reason, cause); } public String ice_name() { return "BadZoneName";Ice 3.4.2 Documentation 326 Copyright © 2011, ZeroC, Inc. } } Each Slice exception is mapped to a Java class with the same name. For each data member, the corresponding class contains a public data member. (Obviously, because and do not have members, the generated classes for these exceptions also doBadTimeVal BadZoneName not have members.) Note that you can optionally for data members to use getters and setters instead.customize the mapping The inheritance structure of the Slice exceptions is preserved for the generated classes, so and inherit from BadTimeVal BadZoneName .GenericError Each exception also defines the member function, which returns the name of the exception.ice_name All user exceptions are derived from the base class . This allows you to catch all user exceptions generically byIce.UserException installing a handler for . , in turn, derives from .Ice.UserException Ice.UserException java.lang.Exception Ice.UserException implements a method that is inherited by its derived exceptions, so you can make memberwise shallowclone copies of exceptions. Note that the generated exception classes contain other member functions that are not shown. However, those member functions are internal to the Java mapping and are not meant to be called by application code. Java Default Constructors for User Exceptions Exceptions have a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize members of structure and enumerated types because the Ice run time does not accept null as a legal value for these types. If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition Exceptions also have a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assign to its members). For derived exceptions, this constructor accepts one argument for each base exception member, plus one argument for each derived exception member, in base-to-derived order. Java Mapping for Run-Time Exceptions The Ice run time throws run-time exceptions for a number of pre-defined error conditions. All run-time exceptions directly or indirectly derive from (which, in turn, derives from ).Ice.LocalException java.lang.RuntimeException Ice.LocalException implements a method that is inherited by its derived exceptions, so you can make memberwise shallowclone copies of exceptions. Recall the for user and run-time exceptions. By catching exceptions at the appropriate point in the hierarchy, you caninheritance diagram handle exceptions according to the category of error they indicate: Ice.LocalException This is the root of the inheritance tree for run-time exceptions. Ice.UserException This is the root of the inheritance tree for user exceptions. Ice.TimeoutException This is the base exception for both operation-invocation and connection-establishment timeouts. Ice.ConnectTimeoutException This exception is raised when the initial attempt to establish a connection to a server times out. For example, a can be handled as , , ConnectTimeoutException ConnectTimeoutException TimeoutException , or .LocalException java.lang.Exception You will probably have little need to catch run-time exceptions as their most-derived type and instead catch them as ; theLocalException fine-grained error handling offered by the remainder of the hierarchy is of interest mainly in the implementation of the Ice run time. Exceptions to this rule are the exceptions related to and life cycles, which you may want to catch explicitly. These exceptions arefacet object and , respectively.FacetNotExistException ObjectNotExistExceptionIce 3.4.2 Documentation 327 Copyright © 2011, ZeroC, Inc. See Also User Exceptions Run-Time Exceptions Java Mapping for Modules Java Mapping for Built-In Types Java Mapping for Enumerations Java Mapping for Structures Java Mapping for Sequences Java Mapping for Dictionaries Java Mapping for Constants JavaBean Mapping Facets and Versioning Object Life CycleIce 3.4.2 Documentation 328 Copyright © 2011, ZeroC, Inc. Java Mapping for Interfaces The mapping of Slice revolves around the idea that, to invoke a remote operation, you call a member function on a local classinterfaces instance that is a for the remote object. This makes the mapping easy and intuitive to use because making a remote procedure call isproxy no different from making a local procedure call (apart from error semantics). On this page: Java Classes Generated for an Interface Proxy Interfaces in Java The Interface in JavaIce.ObjectPrx Proxy Helpers in Java Using Proxy Methods in Java Object Identity and Proxy Comparison in Java Deserializing Proxies in Java Java Classes Generated for an Interface The compiler generates quite a few source files for each Slice interface. In general, for an interface , the following< >interface-name source files are created by the compiler: < >.javainterface-name This source file declares the Java interface.< >interface-name < >Holder.javainterface-name This source file defines a for the interface.holder type < >Prx.javainterface-name This source file defines the .proxy interface < >Prxinterface-name < >PrxHelper.javainterface-name This source file defines the for the interface's proxy.helper type < >PrxHolder.javainterface-name This source file defines the for the interface's proxy.holder type _< >Operations.javainterface-name _< >OperationsNC.javainterface-name These source files each define an interface that contains the operations corresponding to the Slice interface. These are the files that contain code that is relevant to the client side. The compiler also generates a file that is specific to the server side, plus three additional files: _< >Disp.javainterface-name This file contains the definition of the class.server-side skeleton _< >Del.javainterface-name _< >DelD.javainterface-name _< >DelM.javainterface-name These files contain code that is internal to the Java mapping; they do not contain any functions of relevance to application programmers. Proxy Interfaces in Java On the client side, a Slice interface maps to a Java interface with methods that correspond to the operations on that interface. Consider the following simple interface: Slice interface Simple { void op(); }; The Slice compiler generates the following definition for use by the client:Ice 3.4.2 Documentation 329 Copyright © 2011, ZeroC, Inc. Java public interface SimplePrx extends Ice.ObjectPrx { public void op(); public void op(java.util.Map __context); } As you can see, the compiler generates a . In general, the generated name is . If anproxy interface SimplePrx < >Prxinterface-name interface is nested in a module , the generated class is part of package , so the fully-qualified name is .M M M.< >Prxinterface-name In the client's address space, an instance of is the local ambassador for a remote instance of the interface in a serverSimplePrx Simple and is known as a proxy instance. All the details about the server-side object, such as its address, what protocol to use, and its object identity are encapsulated in that instance. Note that inherits from . This reflects the fact that all Ice interfaces implicitly inherit from .SimplePrx Ice.ObjectPrx Ice::Object For each operation in the interface, the proxy class has a method of the same name. For the preceding example, we find that the operation has been mapped to the method . Also note that is overloaded: the second version of has a parameter of type op op op op __context . This parameter is for use by the Ice run time to store information about how to deliver a request.java.util.Map You normally do not need to use it. (We examine the parameter in detail in . The parameter is also used by __context Request Contexts .)IceStorm Because all the types are interfaces, you cannot instantiate an object of such a type. Instead, proxy instances are< >Prxinterface-name always instantiated on behalf of the client by the Ice run time, so client code never has any need to instantiate a proxy directly.The proxy references handed out by the Ice run time are always of type ; the concrete implementation of the interface is part< >Prxinterface-name of the Ice run time and does not concern application code. A value of denotes the null proxy. The null proxy is a dedicated value that indicates that a proxy points "nowhere" (denotes no object).null The Interface in JavaIce.ObjectPrx All Ice objects have as the ultimate ancestor type, so all proxies inherit from . provides a number ofObject Ice.ObjectPrx ObjectPrx methods: Java package Ice; public interface ObjectPrx { boolean equals(java.lang.Object r); Identity ice_getIdentity(); boolean ice_isA(String __id); boolean ice_isA(String __id, java.util.Map ctx); String[] ice_ids(); String[] ice_ids(java.util.Map ctx); String ice_id(); String ice_id(java.util.Map ctx); void ice_ping(); void ice_ping(java.util.Map ctx); // ... } The methods behave as follows: equals This operation compares two proxies for equality. Note that all aspects of proxies are compared by this operation, such as the communication endpoints for the proxy. This means that, in general, if two proxies compare unequal, that does imply that theynot denote different objects. For example, if two proxies denote the same Ice object via different transport endpoints, returns equals even though the proxies denote the same object.false ice_getIdentity This method returns the identity of the object denoted by the proxy. The identity of an Ice object has the following Slice type:Ice 3.4.2 Documentation 330 Copyright © 2011, ZeroC, Inc. Slice module Ice { struct Identity { string name; string category; }; }; To see whether two proxies denote the same object, first obtain the identity for each object and then compare the identities: Java Ice.ObjectPrx o1 = ...; Ice.ObjectPrx o2 = ...; Ice.Identity i1 = o1.ice_getIdentity(); Ice.Identity i2 = o2.ice_getIdentity(); if (i1.equals(i2)) // o1 and o2 denote the same object else // o1 and o2 denote different objects ice_isA The method determines whether the object denoted by the proxy supports a specific interface. The argument to ice_isA ice_isA is a . For example, to see whether a proxy of type denotes a object, we can write:type ID ObjectPrx Printer Java Ice.ObjectPrx o = ...; if (o != null && o.ice_isA("::Printer")) // o denotes a Printer object else // o denotes some other type of object Note that we are testing whether the proxy is null before attempting to invoke the method. This avoids getting a ice_isA if the proxy is null.NullPointerException ice_ids The method returns an array of strings representing all of the type IDs that the object denoted by the proxy supports.ice_ids ice_id The method returns the type ID of the object denoted by the proxy. Note that the type returned is the type of the actualice_id object, which may be more derived than the static type of the proxy. For example, if we have a proxy of type , with a staticBasePrx type ID of , the return value of might be , or it might something more derived, such as .::Base ice_id ::Base ::Derived ice_ping The method provides a basic reachability test for the object. If the object can physically be contacted (that is, the objectice_ping exists and its server is running and reachable), the call completes normally; otherwise, it throws an exception that indicates why the object could not be reached, such as or .ObjectNotExistException ConnectTimeoutException The , , , and methods are remote operations and therefore support an additional overloading thatice_isA ice_ids ice_id ice_ping accepts a . Also note that there are in , not shown here. These methods provide different ways torequest context other methods ObjectPrx dispatch a call and also provide access to an object's .facets Proxy Helpers in Java For each Slice interface, apart from the proxy interface, the Slice-to-Java compiler creates a helper class: for an interface , the nameSimple of the generated helper class is . The helper classes contains two methods that support down-casting:SimplePrxHelperIce 3.4.2 Documentation 331 Copyright © 2011, ZeroC, Inc. Java public final class SimplePrxHelper extends Ice.ObjectPrxHelper implements SimplePrx { public static SimplePrx checkedCast(Ice.ObjectPrx b) { // ... } public static SimplePrx checkedCast(Ice.ObjectPrx b, Ice.Context ctx) { // ... } public static SimplePrx uncheckedCast(Ice.ObjectPrx b) { // ... } // ... } Both the and methods implement a down-cast: if the passed proxy is a proxy for an object of type ,checkedCast uncheckedCast Simple or a proxy for an object with a type derived from , the cast returns a non-null reference to a proxy of type ; otherwise, ifSimple SimplePrx the passed proxy denotes an object of a different type (or if the passed proxy is null), the cast returns a null reference. Given a proxy of any type, you can use a to determine whether the corresponding object supports a given type, for example:checkedCast Java Ice.ObjectPrx obj = ...; // Get a proxy from somewhere... SimplePrx simple = SimplePrxHelper.checkedCast(obj); if (simple != null) // Object supports the Simple interface... else // Object is not of type Simple... Note that a contacts the server. This is necessary because only the implementation of an object in the server has definitecheckedCast knowledge of the type of an object. As a result, a may throw a or an checkedCast ConnectTimeoutException . (This also explains the need for the helper class: the Ice run time must contact the server, so we cannot useObjectNotExistException a Java down-cast.) In contrast, an does not contact the server and unconditionally returns a proxy of the requested type. However, if you douncheckedCast use an , you must be certain that the proxy really does support the type you are casting to; otherwise, if you get it wrong,uncheckedCast you will most likely get a run-time exception when you invoke an operation on the proxy. The most likely error for such a type mismatch is . However, other exceptions, such as a marshaling exception are possible as well. And, if the objectOperationNotExistException happens to have an operation with the correct name, but different parameter types, no exception may be reported at all and you simply end up sending the invocation to an object of the wrong type; that object may do rather nonsensical things. To illustrate this, consider the following two interfaces: Slice interface Process { void launch(int stackSize, int dataSize); }; // ... interface Rocket { void launch(float xCoord, float yCoord); }; Suppose you expect to receive a proxy for a object and use an to down-cast the proxy:Process uncheckedCastIce 3.4.2 Documentation 332 Copyright © 2011, ZeroC, Inc. Java Ice.ObjectPrx obj = ...; // Get proxy... ProcessPrx process = ProcessPrxHelper.uncheckedCast(obj); // No worries... process.launch(40, 60); // Oops... If the proxy you received actually denotes a object, the error will go undetected by the Ice run time: because and haveRocket int float the same size and because the Ice protocol does not tag data with its type on the wire, the implementation of will simplyRocket::launch misinterpret the passed integers as floating-point numbers. In fairness, this example is somewhat contrived. For such a mistake to go unnoticed at run time, both objects must have an operation with the same name and, in addition, the run-time arguments passed to the operation must have a total marshaled size that matches the number of bytes that are expected by the unmarshaling code on the server side. In practice, this is extremely rare and an incorrect uncheckedCast typically results in a run-time exception. A final warning about down-casts: you must use either a or an to down-cast a proxy. If you use a JavacheckedCast uncheckedCast cast, the behavior is undefined. Using Proxy Methods in Java The base proxy class supports a variety of . Since proxies are immutable, each of theseObjectPrx methods for customizing a proxy "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second timeout as shown below: Java Ice.ObjectPrx proxy = communicator.stringToProxy(...); proxy = proxy.ice_timeout(10000); A factory method returns a new proxy object if the requested modification differs from the current proxy, otherwise it returns the current proxy. With few exceptions, factory methods return a proxy of the same type as the current proxy, therefore it is generally not necessary to repeat a or after using a factory method. However, a regular cast is still required, as shown in the examplecheckedCast uncheckedCast below: Java Ice.ObjectPrx base = communicator.stringToProxy(...); HelloPrx hello = HelloPrxHelper.checkedCast(base); hello = (HelloPrx)hello.ice_timeout(10000); # Type is preserved hello.sayHello(); The only exceptions are the factory methods and . Calls to either of these methods may produce a proxy for anice_facet ice_identity object of an unrelated type, therefore they return a base proxy that you must subsequently down-cast to an appropriate type. Object Identity and Proxy Comparison in Java Proxies provide an method that compares proxies:equals Java interface ObjectPrx { boolean equals(java.lang.Object r); } Note that proxy comparison with uses of the information in a proxy for the comparison. This means that not only the objectequals all identity must match for a comparison to succeed, but other details inside the proxy, such as the protocol and endpoint information, must be the same. In other words, comparison with tests for identity, object identity. A common mistake is to write code along theequals proxy not following lines:Ice 3.4.2 Documentation 333 Copyright © 2011, ZeroC, Inc. Java Ice.ObjectPrx p1 = ...; // Get a proxy... Ice.ObjectPrx p2 = ...; // Get another proxy... if (p1.equals(p2)) { // p1 and p2 denote different objects // WRONG! } else { // p1 and p2 denote the same object // Correct } Even though and differ, they may denote the same Ice object. This can happen because, for example, both and embed thep1 p2 p1 p2 same object identity, but each use a different protocol to contact the target object. Similarly, the protocols may be the same, but denote different endpoints (because a single Ice object can be contacted via several different transport endpoints). In other words, if two proxies compare equal with , we know that the two proxies denote the same object (because they are identical in all respects); however, ifequals two proxies compare unequal with , we know absolutely nothing: the proxies may or may not denote the same object.equals To compare the object identities of two proxies, you can use a helper function in the class:Ice.Util Java package Ice; public final class Util { public static int proxyIdentityCompare(ObjectPrx lhs, ObjectPrx rhs); public static int proxyIdentityAndFacetCompare(ObjectPrx lhs, ObjectPrx rhs); // ... } proxyIdentityCompare allows you to correctly compare proxies for identity: Java Ice.ObjectPrx p1 = ...; // Get a proxy... Ice.ObjectPrx p2 = ...; // Get another proxy... if (Ice.Util.proxyIdentityCompare(p1, p2) != 0) { // p1 and p2 denote different objects // Correct } else { // p1 and p2 denote the same object // Correct } The function returns 0 if the identities are equal, if is less than , and 1 if is greater than . (The comparison uses as the-1 p1 p2 p1 p2 name major and as the minor sort key.)category The function behaves similarly, but compares both the identity and the .proxyIdentityAndFacetCompare facet name In addition, the Java mapping provides two wrapper classes that allow you to wrap a proxy for use as the key of a hashed collection:Ice 3.4.2 Documentation 334 Copyright © 2011, ZeroC, Inc. Java package Ice; public class ProxyIdentityKey { public ProxyIdentityKey(Ice.ObjectPrx proxy); public int hashCode(); public boolean equals(java.lang.Object obj); public Ice.ObjectPrx getProxy(); } public class ProxyIdentityFacetKey { public ProxyIdentityFacetKey(Ice.ObjectPrx proxy); public int hashCode(); public boolean equals(java.lang.Object obj); public Ice.ObjectPrx getProxy(); } The constructor caches the identity and the hash code of the passed proxy, so calls to and can be evaluated efficiently.hashCode equals The method returns the proxy that was passed to the constructor.getProxy As for the comparison functions, only uses the proxy's identity, whereas also includes theProxyIdentityKey ProxyIdentityFacetKey facet name. Deserializing Proxies in Java Proxy objects implement the interface that enables serialization of proxies to and from a byte stream. You canjava.io.Serializable use the standard class to deserialize all Slice types proxies; proxies are a special case becausejava.io.ObjectInputStream except they must be created by a communicator. To supply a communicator for use in deserializing proxies, an application must use the class :Ice.ObjectInputStream Java package Ice; public class ObjectInputStream extends java.io.ObjectInputStream { public ObjectInputStream(Communicator communicator, java.io.InputStream stream) throws java.io.IOException; public Communicator getCommunicator(); } The code shown below demonstrates how to use this class: Java Ice.Communicator communicator = ... byte[] bytes = ... // data to be deserialized java.io.ByteArrayInputStream byteStream = new java.io.ByteArrayInputStream(bytes); Ice.ObjectInputStream in = new Ice.ObjectInputStream(communicator, byteStream); Ice.ObjectPrx proxy = (Ice.ObjectPrx)in.readObject(); Ice raises if an application attempts to deserialize a proxy without supplying a communicator.java.io.IOException See Also Interfaces, Operations, and Exceptions Proxies Type IDs Java Mapping for OperationsIce 3.4.2 Documentation 335 Copyright © 2011, ZeroC, Inc. Request Contexts Operations on Object Proxy Methods Facets and Versioning IceStormIce 3.4.2 Documentation 336 Copyright © 2011, ZeroC, Inc. Java Mapping for Operations On this page: Basic Java Mapping for Operations Normal and Operations in Javaidempotent Passing Parameters in Java In-Parameters in Java Out-Parameters in Java Null Parameters in Java Exception Handling in Java Exceptions and Out-Parameters Basic Java Mapping for Operations As we saw in the , for each on an interface, the proxy class contains a corresponding member function withmapping for interfaces operation the same name. To invoke an operation, you call it via the proxy. For example, here is part of the definitions for our :file system Slice module Filesystem { interface Node { idempotent string name(); }; // ... }; The operation returns a value of type . Given a proxy to an object of type , the client can invoke the operation as follows:name string Node Java NodePrx node = ...; // Initialize proxy String name = node.name(); // Get name via RPC This illustrates the typical pattern for receiving return values: return values are returned by reference for complex types, and by value for simple types (such as or ).int double Normal and Operations in Javaidempotent You can add an qualifier to a Slice operation. As far as the signature for the corresponding proxy method is concerned, idempotent has no effect. For example, consider the following interface:idempotent Slice interface Example { string op1(); idempotent string op2(); }; The proxy interface for this is: Java public interface ExamplePrx extends Ice.ObjectPrx { public String op1(); public String op2(); } Because affects an aspect of call dispatch, not interface, it makes sense for the two methods to be mapped the same.idempotentIce 3.4.2 Documentation 337 Copyright © 2011, ZeroC, Inc. Passing Parameters in Java In-Parameters in Java The parameter passing rules for the Java mapping are very simple: parameters are passed either by value (for simple types) or by reference (for complex types and type ). Semantically, the two ways of passing parameters are identical: it is guaranteed that the value of astring parameter will not be changed by the invocation (with some caveats — see ).Location Transparency Here is an interface with operations that pass parameters of various types from client to server: Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ClientToServer { void op1(int i, float f, bool b, string s); void op2(NumberAndString ns, StringSeq ss, StringTable st); void op3(ClientToServer* proxy); }; The Slice compiler generates the following proxy for these definitions: Java public interface ClientToServerPrx extends Ice.ObjectPrx { public void op1(int i, float f, boolean b, String s); public void op2(NumberAndString ns, String[] ss, java.util.Map st); public void op3(ClientToServerPrx proxy); } Given a proxy to a interface, the client code can pass parameters as in the following example:ClientToServer Java ClientToServerPrx p = ...; // Get proxy... p.op1(42, 3.14f, true, "Hello world!"); // Pass simple literals int i = 42; float f = 3.14f; boolean b = true; String s = "Hello world!"; p.op1(i, f, b, s); // Pass simple variables NumberAndString ns = new NumberAndString(); ns.x = 42; ns.str = "The Answer"; String[] ss = { "Hello world!" }; java.util.HashMap st = new java.util.HashMap(); st.put(new Long(0), ns); p.op2(ns, ss, st); // Pass complex variables p.op3(p); // Pass proxyIce 3.4.2 Documentation 338 Copyright © 2011, ZeroC, Inc. Out-Parameters in Java Java does not have pass-by-reference: parameters are always passed by value. For a function to modify one of its arguments, we must pass a reference (by value) to an object; the called function can then modify the object's contents via the passed reference. To permit the called function to modify a parameter, the Java mapping uses so-called classes. For example, for each of the built-inholder Slice types, such as and , the package contains a corresponding holder class. Here are the definitions for the holderint string Ice classes and :Ice.IntHolder Ice.StringHolder Java package Ice; public final class IntHolder { public IntHolder() {} public IntHolder(int value) this.value = value; } public int value; } public final class StringHolder { public StringHolder() {} public StringHolder(String value) { this.value = value; } public String value; } A holder class has a public member that stores the value of the parameter; the called function can modify the value by assigning tovalue that member. The class also has a default constructor and a constructor that accepts an initial value. For user-defined types, such as structures, the Slice-to-Java compiler generates a corresponding holder type. For example, here is the generated holder type for the structure we defined earlier:NumberAndString Java public final class NumberAndStringHolder { public NumberAndStringHolder() {} public NumberAndStringHolder(NumberAndString value) { this.value = value; } public NumberAndString value; } This looks exactly like the holder classes for the built-in types: we get a default constructor, a constructor that accepts an initial value, and the public member.value Note that holder classes are generated for Slice type you define. For example, for sequences, such as the sequence,every FruitPlatter the compiler does not generate a special Java type because sequences map to Java arrays. However, the compiler doesFruitPlatter generate a class, so we can pass a array as an out-parameter.FruitPlatterHolder FruitPlatter To pass an out-parameter to an operation, we simply pass an instance of a holder class and examine the member of eachvalue out-parameter when the call completes. Here are the same Slice definitions we saw earlier, but this time with all parameters being passed in the direction:outIce 3.4.2 Documentation 339 Copyright © 2011, ZeroC, Inc. Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ServerToClient { void op1(out int i, out float f, out bool b, out string s); void op2(out NumberAndString ns, out StringSeq ss, out StringTable st); void op3(out ServerToClient* proxy); }; The Slice compiler generates the following code for these definitions: Java public interface ClientToServerPrx extends Ice.ObjectPrx { public void op1(Ice.IntHolder i, Ice.FloatHolder f, Ice.BooleanHolder b, Ice.StringHolder s); public void op2(NumberAndStringHolder ns, StringSeqHolder ss, StringTableHolder st); public void op3(ClientToServerPrxHolder proxy); } Given a proxy to a interface, the client code can pass parameters as in the following example:ServerToClient Java ClientToServerPrx p = ...; // Get proxy... Ice.IntHolder ih = new Ice.IntHolder(); Ice.FloatHolder fh = new Ice.FloatHolder(); Ice.BooleanHolder bh = new Ice.BooleanHolder(); Ice.StringHolder sh = new Ice.StringHolder(); p.op1(ih, fh, bh, sh); NumberAndStringHolder nsh = new NumberAndString(); StringSeqHolder ssh = new StringSeqHolder(); StringTableHolder sth = new StringTableHolder(); p.op2(nsh, ssh, sth); ServerToClientPrxHolder stcph = new ServerToClientPrxHolder(); p.op3(stch); System.out.writeln(ih.value); // Show one of the values Again, there are no surprises in this code: the various holder instances contain values once the operation invocation completes and the member of each instance provides access to those values.value Null Parameters in Java Some Slice types naturally have "empty" or "not there" semantics. Specifically, sequences, dictionaries, and strings all can be , but thenull corresponding Slice types do not have the concept of a null value. To make life with these types easier, whenever you pass as anull parameter or return value of type sequence, dictionary, or string, the Ice run time automatically sends an empty sequence, dictionary, orIce 3.4.2 Documentation 340 Copyright © 2011, ZeroC, Inc. string to the receiver. This behavior is useful as a convenience feature: especially for deeply-nested data types, members that are sequences, dictionaries, or strings automatically arrive as an empty value at the receiving end. This saves you having to explicitly initialize, for example, every string element in a large sequence before sending the sequence in order to avoid . Note that using null parameters inNullPointerException this way does create null semantics for Slice sequences, dictionaries, or strings. As far as the object model is concerned, these do notnot exist (only sequences, dictionaries, and strings do). For example, whether you send a string as or as an empty string makes noempty null difference to the receiver: either way, the receiver sees an empty string. Exception Handling in Java Any operation invocation may throw a and, if the operation has an exception specification, may also throw run-time exception user . Suppose we have the following simple interface:exceptions Slice exception Tantrum { string reason; }; interface Child { void askToCleanUp() throws Tantrum; }; Slice exceptions are thrown as Java exceptions, so you can simply enclose one or more operation invocations in a - block:try catch Java ChildPrx child = ...; // Get child proxy... try { child.askToCleanUp(); } catch (Tantrum t) { System.out.write("The child says: "); System.out.writeln(t.reason); } Typically, you will catch only a few exceptions of specific interest around an operation invocation; other exceptions, such as unexpected run-time errors, will typically be handled by exception handlers higher in the hierarchy. For example:Ice 3.4.2 Documentation 341 Copyright © 2011, ZeroC, Inc. 1. Java public class Client { static void run() { ChildPrx child = ...; // Get child proxy... try { child.askToCleanUp(); } catch (Tantrum t) { System.out.print("The child says: "); System.out.println(t.reason); child.scold(); // Recover from error... } child.praise(); // Give positive feedback... } public static void main(String[] args) { try { // ... run(); // ... } catch (Ice.LocalException e) { e.printStackTrace(); } catch (Ice.UserException e) { System.err.println(e.getMessage()); } } } This code handles a specific exception of local interest at the point of call and deals with other exceptions generically. (This is also the strategy we used for our .)first simple application Exceptions and Out-Parameters The Ice run time makes no guarantees about the state of out-parameters when an operation throws an exception: the parameter may still have its original value or may have been changed by the operation's implementation in the target object. In other words, for out-parameters, Ice provides the weak exception guarantee but does not provide the strong exception guarantee.[1] This is done for reasons of efficiency: providing the strong exception guarantee would require more overhead than can be justified. See Also Operations Java Mapping for Exceptions Java Mapping for Sequences Java Mapping for Interfaces Location Transparency References Sutter, H. 1999. . Reading, MA: Addison-Wesley.Exceptional C++: 47 Engineering Puzzles, Programming Problems, and SolutionsIce 3.4.2 Documentation 342 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. Java Mapping for Classes On this page: Basic Java Mapping for Classes Operations Interfaces in Java Inheritance from in JavaIce.Object Class Data Members in Java Class Operations in Java Class Factories in Java Class Constructors in Java Basic Java Mapping for Classes A Slice is mapped to a Java class with the same name. The generated class contains a public data member for each Slice dataclass member (just as for structures and exceptions), and a member function for each operation. Consider the following class definition: Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; The Slice compiler generates the following code for this definition: Java public interface _TimeOfDayOperations { String format(Ice.Current current); } public interface _TimeOfDayOperationsNC { String format(); } public abstract class TimeOfDay extends Ice.ObjectImpl implements _TimeOfDayOperations, _TimeOfDayOperationsNC { public short hour; public short minute; public short second; public TimeOfDay(); public TimeOfDay(short hour, short minute, short second); // ... } There are a number of things to note about the generated code: The compiler generates "operations interfaces" called and . These_TimeOfDayOperations _TimeOfDayOperationsNC interfaces contain a method for each Slice operation of the class. The generated class inherits (indirectly) from . This means that all classes implicitly inherit from TimeOfDay Ice.Object , which is the ultimate ancestor of all classes. Note that is the same as . In otherIce.Object Ice.Object not Ice.ObjectPrx words, you pass a class where a proxy is expected and vice versa. cannot If a class has only data members, but no operations, the compiler generates a non-abstract class. The generated class contains a public member for each Slice data member. The generated class inherits member functions for each Slice operation from the operations interfaces. The generated class contains two constructors. There is quite a bit to discuss here, so we will look at each item in turn.Ice 3.4.2 Documentation 343 Copyright © 2011, ZeroC, Inc. Operations Interfaces in Java The methods in the interface have an additional trailing parameter of type , whereas the_< >Operationsinterface-name Ice.Current methods in the interface lack this additional trailing parameter. The methods without the _< >OperationsNCinterface-name Current parameter simply forward to the methods with a parameter, supplying a default . For now, you can ignore this parameterCurrent Current and pretend it does not exist. If a class has only data members, but no operations, the compiler omits generating the and ><_interface-name Operations _< interfaces.>OperationsNCinterface-name Inheritance from in JavaIce.Object Like interfaces, classes implicitly inherit from a common base class, . However, as shown in the illustration below, classesIce.Object inherit from instead of (which is at the base of the inheritance hierarchy for proxies). As a result, you cannotIce.Object Ice.ObjectPrx pass a class where a proxy is expected (and vice versa) because the base types for classes and proxies are not compatible. Inheritance from and .Ice.ObjectPrx Ice.Object Ice.Object contains a number of member functions: Java package Ice; public interface Object { boolean ice_isA(String s); boolean ice_isA(String s, Current current); void ice_ping(); void ice_ping(Current current); String[] ice_ids(); String[] ice_ids(Current current); String ice_id(); String ice_id(Current current); void ice_preMarshal(); void ice_postUnmarshal(); DispatchStatus ice_dispatch(Request request, DispatchInterceptorAsyncCallback cb); } The member functions of behave as follows:Ice.Object ice_isA This function returns if the object supports the given , and otherwise.true type ID false ice_ping As for interfaces, provides a basic reachability test for the class.ice_ping ice_idsIce 3.4.2 Documentation 344 Copyright © 2011, ZeroC, Inc. This function returns a string sequence representing all of the supported by this object, including .type IDs ::Ice::Object ice_id This function returns the actual run-time for a class. If you call through a reference to a base instance, the returnedtype ID ice_id type id is the actual (possibly more derived) type ID of the instance. ice_preMarshal The Ice run time invokes this function prior to marshaling the object's state, providing the opportunity for a subclass to validate its declared data members. ice_postUnmarshal The Ice run time invokes this function after unmarshaling an object's state. A subclass typically overrides this function when it needs to perform additional initialization using the values of its declared data members. ice_dispatch This function dispatches an incoming request to a servant. It is used in the implementation of .dispatch interceptors Note that the generated class does not override and . This means that classes are compared using shallow referencehashCode equals equality, not value equality (as is used for structures). All Slice classes derive from via the abstract base class. implements the Ice.Object Ice.ObjectImpl ObjectImpl interface to support Java's facility. also supplies an implementation of thatjava.io.Serializable serialization ObjectImpl clone returns a shallow memberwise copy. Class Data Members in Java By default, data members of classes are mapped exactly as for structures and exceptions: for each data member in the Slice definition, the generated class contains a corresponding public data member. If you wish to restrict access to a data member, you can modify its visibility using the metadata directive. The presence of thisprotected directive causes the Slice compiler to generate the data member with protected visibility. As a result, the member can be accessed only by the class itself or by one of its subclasses. For example, the class shown below has the metadata directive appliedTimeOfDay protected to each of its data members: Slice class TimeOfDay { ["protected"] short hour; // 0 - 23 ["protected"] short minute; // 0 - 59 ["protected"] short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; The Slice compiler produces the following generated code for this definition: Java public abstract class TimeOfDay extends Ice.ObjectImpl implements _TimeOfDayOperations, _TimeOfDayOperationsNC { protected short hour; protected short minute; protected short second; public TimeOfDay(); public TimeOfDay(short hour, short minute, short second); // ... } For a class in which all of the data members are protected, the metadata directive can be applied to the class itself rather than to each member individually. For example, we can rewrite the class as follows:TimeOfDayIce 3.4.2 Documentation 345 Copyright © 2011, ZeroC, Inc. Slice ["protected"] class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; Note that you can optionally for data members to use getters and setters instead.customize the mapping Class Operations in Java Operations of classes are mapped to abstract member functions in the generated class. This means that, if a class contains operations (such as the operation of our class), you must provide an implementation of the operation in a class that is derived from theformat TimeOfDay generated class. For example: Java public class TimeOfDayI extends TimeOfDay { public String format(Ice.Current current) { DecimalFormat df = (DecimalFormat)DecimalFormat.getInstance(); df.setMinimumIntegerDigits(2); return new String(df.format(hour) + ":" + df.format(minute) + ":" + df.format(second)); } } Class Factories in Java Having created a class such as , we have an implementation and we can instantiate the class, but we cannotTimeOfDayI TimeOfDayI receive it as the return value or as an out-parameter from an operation invocation. To see why, consider the following simple interface: Slice interface Time { TimeOfDay get(); }; When a client invokes the operation, the Ice run time must instantiate and return an instance of the class. However, get TimeOfDay is an abstract class that cannot be instantiated. Unless we tell it, the Ice run time cannot magically know that we have created a TimeOfDay class that implements the abstract operation of the abstract class. In other words, we must provide theTimeOfDayI format TimeOfDay Ice run time with a factory that knows that the abstract class has a concrete implementation. The TimeOfDay TimeOfDayI interface provides us with the necessary operations:Ice::Communicator Slice module Ice { local interface ObjectFactory { Object create(string type); void destroy(); }; local interface Communicator { void addObjectFactory(ObjectFactory factory, string id); ObjectFactory findObjectFactory(string id); // ... }; };Ice 3.4.2 Documentation 346 Copyright © 2011, ZeroC, Inc. To supply the Ice run time with a factory for our class, we must implement the interface:TimeOfDayI ObjectFactory Java class ObjectFactory implements Ice.ObjectFactory { public Ice.Object create(String type) { if (type.equals(M.TimeOfDay.ice_staticId())) { return new TimeOfDayI(); } assert(false); return null; } public void destroy() { // Nothing to do } } The object factory's method is called by the Ice run time when it needs to instantiate a class. The factory's create TimeOfDay destroy method is called by the Ice run time when its communicator is destroyed. The method is passed the of the class to instantiate. For our class, the type ID is . Ourcreate type ID TimeOfDay "::M::TimeOfDay" implementation of checks the type ID: if it matches, the method instantiates and returns a object. For other type IDs,create TimeOfDayI the method asserts because it does not know how to instantiate other types of objects. Note that we used the method to obtain the type ID rather than embedding a literal string. Using a literal type ID string inice_staticId your code is discouraged because it can lead to errors that are only detected at run time. For example, if a Slice class or one of its enclosing modules is renamed and the literal string is not changed accordingly, a receiver will fail to unmarshal the object and the Ice run time will raise . By using instead, we avoid any risk of a misspelled or obsolete type ID, and we canNoObjectFactoryException ice_staticId discover at compile time if a Slice class or module has been renamed. Given a factory implementation, such as our , we must inform the Ice run time of the existence of the factory:ObjectFactory Java Ice.Communicator ic = ...; ic.addObjectFactory(new ObjectFactory(), M.TimeOfDay.ice_staticId()); Now, whenever the Ice run time needs to instantiate a class with the type ID , it calls the method of the"::M::TimeOfDay" create registered instance.ObjectFactory The operation of the object factory is invoked by the Ice run time when the communicator is destroyed. This gives you a chance todestroy clean up any resources that may be used by your factory. Do not call on the factory while it is registered with the communicator —destroy if you do, the Ice run time has no idea that this has happened and, depending on what your implementation is doing, may causedestroy undefined behavior when the Ice run time tries to next use the factory. The run time guarantees that will be the last call made on the factory, that is, will not be called concurrently with destroy create destroy , and will not be called once has been called. However, calls to can be made concurrently.create destroy create Note that you cannot register a factory for the same type ID twice: if you call with a type ID for which a factory isaddObjectFactory registered, the Ice run time throws an .AlreadyRegisteredException Finally, keep in mind that if a class has only data members, but no operations, you need not create and register an object factory to transmit instances of such a class. Only if a class has operations do you have to define and register an object factory. Class Constructors in Java Classes have a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize members of structure and enumerated types because the Ice run time does not accept null as a legal value for these types. If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition The generated class also contains a second constructor that accepts one argument for each member of the class. This allows you to create and initialize a class in a single statement, for example:Ice 3.4.2 Documentation 347 Copyright © 2011, ZeroC, Inc. Java TimeOfDayI tod = new TimeOfDayI(14, 45, 00); // 14:45pm For derived classes, the constructor requires an argument for every member of the class, including inherited members. For example, consider the the definition from once more:Class Inheritance Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; class DateTime extends TimeOfDay { short day; // 1 - 31 short month; // 1 - 12 short year; // 1753 onwards }; The constructors for the generated classes are as follows: Java public class TimeOfDay extends Ice.ObjectImpl { public TimeOfDay() {} public TimeOfDay(short hour, short minute, short second) { this.hour = hour; this.minute = minute; this.second = second; } // ... } public class DateTime extends TimeOfDay { public DateTime() { super(); } public DateTime(short hour, short minute, short second, short day, short month, short year) { super(hour, minute, second); this.day = day; this.month = month; this.year = year; } // ... } If you want to instantiate and initialize a instance, you must either use the default constructor or provide values for all of the dataDateTime members of the instance, including data members of any base classes. See AlsoIce 3.4.2 Documentation 348 Copyright © 2011, ZeroC, Inc. Classes Class Inheritance Type IDs Serializable Objects in Java JavaBean Mapping The Current Object Dispatch InterceptorsIce 3.4.2 Documentation 349 Copyright © 2011, ZeroC, Inc. Serializable Objects in Java In Java terminology, a typically refers to an object that implements the interface and thereforeserializable object java.io.Serializable supports serialization to and from a byte stream. All Java classes generated from Slice definitions implement the java.io.Serializable interface. In addition to serializing Slice types, applications may also need to incorporate foreign types into their Slice definitions. Ice allows you to pass Java directly as operation parameters or as fields of another data type. For example:serializable objects Slice ["java:serializable:SomePackage.JavaClass"] sequence JavaObj; struct MyStruct { int i; JavaObj o; }; interface Example { void op(JavaObj inObj, MyStruct s, out JavaObj outObj); }; The generated code for contains a member of type and a member of type :MyStruct i int o SomePackage.JavaClass Java public final class MyStruct implements java.lang.Cloneable { public int i; public SomePackage.JavaClass o; // ... } Similarly, the signature for has parameters of type and for the in-parameters, and op JavaClass MyStruct for the out-parameter. (Out-parameters are always passed as .)Ice.Holder Ice.Holder< >class Java void op(SomePackage.JavaClass inObj, MyStruct s, Ice.Holder outObj); Of course, your client and server code must have an implementation of that derives from :JavaClass java.io.Serializable Java package SomePackage; public class JavaClass implements java.io.Serializable { // ... } You can implement this class in any way you see fit — the Ice run time does not place any other requirements on the implementation. See Also Serializable ObjectsIce 3.4.2 Documentation 350 Copyright © 2011, ZeroC, Inc. 1. 2. Customizing the Java Mapping You can customize the code that the Slice-to-Java compiler produces by annotating your Slice definitions with . This sectionmetadata describes how metadata influences several aspects of the generated Java code. On this page: Java Packages Java Package Configuration Properties Custom Types in Java Metadata in Java Defining a Custom Sequence Type in Java Defining a Custom Dictionary Type in Java Using Custom Type Metadata in Java Mapping for Modified Out Parameters in Java JavaBean Mapping JavaBean Generated Methods JavaBean Metadata Java Packages By default, the scope of a Slice definition determines the package of its mapped Java construct. A Slice type defined in a module hierarchy is to a type residing in the equivalent Java package.mapped There are times when applications require greater control over the packaging of generated Java classes. For instance, a company may have software development guidelines that require all Java classes to reside in a designated package. One way to satisfy this requirement is to modify the Slice module hierarchy so that the generated code uses the required package by default. In the example below, we have enclosed the original definition of in the modules so that the compiler will create the class in the Workflow::Document com::acme package:com.acme Slice module com { module acme { module Workflow { class Document { // ... }; }; }; }; There are two problems with this workaround: It incorporates the requirements of an implementation language into the application's interface specification. Developers using other languages, such as C++, are also affected. The Slice-to-Java compiler provides a better way to control the packages of generated code through the use of . Theglobal metadata example above can be converted as follows: Slice [["java:package:com.acme"]] module Workflow { class Document { // ... }; }; The global metadata directive instructs the compiler to generate all of the classes resulting from definitions injava:package:com.acme this Slice file into the Java package . The net effect is the same: the class for is generated in the package com.acme DocumentIce 3.4.2 Documentation 351 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. . However, we have addressed the two shortcomings of the first solution by reducing our impact on the interfacecom.acme.Workflow specification: the Slice-to-Java compiler recognizes the package metadata directive and modifies its actions accordingly, whereas the compilers for other language mappings simply ignore it. Java Package Configuration Properties Using global metadata to alter the default package of generated classes has ramifications for the Ice run time when unmarshaling exceptions and . The Ice run time dynamically loads generated classes by translating their Slice type ids into Java class names. Forconcrete class types example, the Ice run time translates the Slice type id into the class name .::Workflow::Document Workflow.Document However, when the generated classes are placed in a user-specified package, the Ice run time can no longer rely on the direct translation of a Slice type id into a Java class name, and therefore requires additional configuration so that it can successfully locate the generated classes. Two configuration properties are supported: Ice.Package.Module=package Associates a top-level Slice module with the package in which it was generated. Only top-level module names are allowed; the semantics of global metadata prevent a nested module from being generated into a different package than its enclosing module. Ice.Default.Package=package Specifies a default package to use if other attempts to load a class have failed. The behavior of the Ice run time when unmarshaling an exception or concrete class is described below: Translate the Slice type id into a Java class name and attempt to load the class. If that fails, extract the top-level module from the type id and check for an property with a matching module name. IfIce.Package found, prepend the specified package to the class name and try to load the class again. If that fails, check for the presence of . If found, prepend the specified package to the class name and tryIce.Default.Package to load the class again. If the class still cannot be loaded, the instance may be .sliced Continuing our example from the previous section, we can define the following property: Ice.Package.Workflow=com.acme Alternatively, we could achieve the same result with this property: Ice.Default.Package=com.acme Custom Types in Java One of the more powerful applications of metadata is the ability to tailor the Java mapping for sequence and dictionary types to match the needs of your application. Metadata in Java The metadata for specifying a custom type has the following format: java:type:instance-type[:formal-type] The formal type is optional; the compiler uses a default value if one is not defined. The instance type must satisfy an is-A relationship with the formal type: either the same class is specified for both types, or the instance type must be derived from the formal type. The Slice-to-Java compiler generates code that uses the formal type for all occurrences of the modified Slice definition except when the generated code must instantiate the type, in which case the compiler uses the instance type instead. The compiler performs no validation on your custom types. Misspellings and other errors will not be apparent until you compile the generated code.Ice 3.4.2 Documentation 352 Copyright © 2011, ZeroC, Inc. Defining a Custom Sequence Type in Java Although the default mapping of a sequence type to a native Java array is efficient and typesafe, it is not always the most convenient representation of your data. To use a different representation, specify the type information in a metadata directive, as shown in the following example: Slice ["java:type:java.util.LinkedList"] sequence StringList; It is your responsibility to use a type parameter for the Java class ( in the example above) that is the correct mapping for theString sequence's element type. The compiler requires the formal type to implement , where is the Java mapping of the element type. If you do notjava.util.List< >EE specify a formal type, the compiler uses by default.java.util.List< >E Note that extra care must be taken when defining custom types that contain nested generic types, such as a custom sequence whose element type is also a custom sequence. The Java compiler strictly enforces type safety, therefore any compatibility issues in the custom type metadata will be apparent when the generated code is compiled. Defining a Custom Dictionary Type in Java The default instance type for a dictionary is , where is the Java mapping of the key type and is the Javajava.util.HashMap< , >KVK V mapping of the value type. If the semantics of a are not suitable for your application, you can specify an alternate type usingHashMap metadata as shown in the example below: Slice ["java:type:java.util.TreeMap"] dictionary StringMap; It is your responsibility to use type parameters for the Java class ( in the example above) that are the correct mappings for theString dictionary's key and value types. The compiler requires the formal type to implement . If you do not specify a formal type, the compiler uses this typejava.util.Map< , >KV by default. Note that extra care must be taken when defining dictionary types that contain nested generic types, such as a dictionary whose element type is a custom sequence. The Java compiler strictly enforces type safety, therefore any compatibility issues in the custom type metadata will be apparent when the generated code is compiled. Using Custom Type Metadata in Java You can define custom type metadata in a variety of situations. The simplest scenario is specifying the metadata at the point of definition: Slice ["java:type:java.util.LinkedList"] sequence StringList; Defined in this manner, the Slice-to-Java compiler uses (the default formal type) for all occurrences of java.util.List , and when it needs to instantiate .StringList java.util.LinkedList StringList You may also specify a custom type more selectively by defining metadata for a data member, parameter or return value. For instance, the mapping for the original Slice definition might be sufficient in most situations, but a different mapping is more convenient in particular cases. The example below demonstrates how to override the sequence mapping for the data member of a structure as well as for several operations:Ice 3.4.2 Documentation 353 Copyright © 2011, ZeroC, Inc. Slice sequence StringSeq; struct S { ["java:type:java.util.LinkedList"] StringSeq seq; }; interface I { ["java:type:java.util.ArrayList"] StringSeq modifiedReturnValue(); void modifiedInParam(["java:type:java.util.ArrayList"] StringSeq seq); void modifiedOutParam(out ["java:type:java.util.ArrayList"] StringSeq seq); }; As you might expect, modifying the mapping for an operation's parameters or return value may require the application to manually convert values from the original mapping to the modified mapping. For example, suppose we want to invoke the operation. ThemodifiedInParam signature of its proxy operation is shown below: Java void modifiedInParam(java.util.List seq, Ice.Current curr) The metadata changes the mapping of the parameter to , which is the default formal type. If a caller has a seq java.util.List value in the original mapping, it must convert the array as shown in the following example:StringSeq Java String[] seq = new String[2]; seq[0] = "hi"; seq[1] = "there"; IPrx proxy = ...; proxy.modifiedInParam(java.util.Arrays.asList(seq)); Although we specified the instance type for the parameter, we are still able to pass the result of java.util.ArrayList because its return type ( ) is compatible with the parameter's formal type declared by the proxyasList java.util.List method. In the case of an operation parameter, the instance type is only relevant to a servant implementation, which may need to make assumptions about the actual type of the parameter. Mapping for Modified Out Parameters in Java The mapping for an parameter uses a generated "holder" class to convey the . If you modify the mapping of an out parameter value out parameter, as discussed in the previous section, it is possible that the holder class for the parameter's unmodified type is no longer compatible with the custom type you have specified. The holder class generated for is shown below:StringSeqIce 3.4.2 Documentation 354 Copyright © 2011, ZeroC, Inc. Java public final class StringSeqHolder { public StringSeqHolder() { } public StringSeqHolder(String[] value) { this.value = value; } public String[] value; } An parameter of type would normally map to a proxy method that used to hold the parameter value.out StringSeq StringSeqHolder When the parameter is modified, as is the case with the operation, the Slice-to-Java compiler cannot use modifiedOutParam to hold an instance of , because is only appropriate for the defaultStringSeqHolder java.util.List StringSeqHolder mapping to a native array. As a result, the compiler handles these situations using instances of the generic class , where is the parameter's formalIce.Holder< >TT type. Consider the following example: Slice sequence StringSeq; interface I { void modifiedOutParam(out ["java:type:java.util.ArrayList"] StringSeq seq); }; The compiler generates the following mapping for the proxy method:modifiedOutParam Java void modifiedOutParam(Ice.Holder > seq, Ice.Current curr) The formal type of the parameter is , therefore the holder class becomes java.util.List .Ice.Holder> JavaBean Mapping The Java mapping optionally generates JavaBean-style methods for the data members of class, structure, and exception types. JavaBean Generated Methods For each data member of type , the mapping generates the following methods:val T Java public T getVal(); public void setVal(T v); The mapping generates an additional method if is the type:T boolIce 3.4.2 Documentation 355 Copyright © 2011, ZeroC, Inc. Java public boolean isVal(); Finally, if is a sequence type with an element type , two methods are generated to provide direct access to elements:T E Java public E getVal(int index); public void setVal(int index, E v); Note that these element methods are only generated for sequence types that use the default mapping. The Slice-to-Java compiler considers it a fatal error for a JavaBean method of a class data member to conflict with a declared operation of the class. In this situation, you must rename the operation or the data member, or disable the generation of JavaBean methods for the data member in question. JavaBean Metadata The JavaBean methods are generated for a data member when the member or its enclosing type is annotated with the java:getset metadata. The following example demonstrates both styles of usage: Slice sequence IntSeq; class C { ["java:getset"] int i; double d; }; ["java:getset"] struct S { bool b; string str; }; ["java:getset"] exception E { IntSeq seq; }; JavaBean methods are generated for all members of struct and exception , but for only one member of class . Relevant portions of theS E C generated code are shown below: Java public class C extends Ice.ObjectImpl { ... public int i; public int getI() { return i; } public void setI(int _i)Ice 3.4.2 Documentation 356 Copyright © 2011, ZeroC, Inc. { i = _i; } public double d; } public final class S implements java.lang.Cloneable { public boolean b; public boolean getB() { return b; } public void setB(boolean _b) { b = _b; } public boolean isB() { return b; } public String str; public String getStr() { return str; } public void setStr(String _str) { str = _str; } ... } public class E extends Ice.UserException { ... public int[] seq; public int[] getSeq() { return seq; } public void setSeq(int[] _seq) { seq = _seq; } public int getSeq(int _index)Ice 3.4.2 Documentation 357 Copyright © 2011, ZeroC, Inc. { return seq[_index]; } public void setSeq(int _index, int _val) { seq[_index] = _val; }Ice 3.4.2 Documentation 358 Copyright © 2011, ZeroC, Inc. ... } See Also Metadata Java Mapping for Modules Java Mapping for Operations Class Inheritance SemanticsIce 3.4.2 Documentation 359 Copyright © 2011, ZeroC, Inc. Asynchronous Method Invocation (AMI) in Java Asynchronous Method Invocation (AMI) is the term used to describe the client-side support for the asynchronous programming model. AMI supports both oneway and twoway requests, but unlike their synchronous counterparts, AMI requests never block the calling thread. When a client issues an AMI request, the Ice run time hands the message off to the local transport buffer or, if the buffer is currently full, queues the request for later delivery. The application can then continue its activities and poll or wait for completion of the invocation, or receive a callback when the invocation completes. AMI is transparent to the server: there is no way for the server to tell whether a client sent a request synchronously or asynchronously. As of version 3.4, Ice provides a new API for asynchronous method invocation. This page describes the new API. Note that the is deprecated and will be removed in a future release.old API On this page: Basic Asynchronous API in Java Asynchronous Proxy Methods in Java Asynchronous Exception Semantics in Java Class in JavaAsyncResult Polling for Completion in Java Generic Completion Callbacks in Java Sharing State Between and Methods in Javabegin_ end_ Type-Safe Completion Callbacks in Java Asynchronous Oneway Invocations in Java Flow Control in Java Asynchronous Batch Requests in Java Concurrency Semantics for AMI in Java AMI Limitations in Java Basic Asynchronous API in Java Consider the following simple Slice definition: Slice module Demo { interface Employees { string getName(int number); }; }; Asynchronous Proxy Methods in Java Besides the synchronous proxy methods, generates the following asynchronous proxy methods:slice2java Java public interface EmployeesPrx extends Ice.ObjectPrx { // ... public Ice.AsyncResult begin_getName(int number); public Ice.AsyncResult begin_getName(int number, java.util.Map __ctx); public String end_getName(Ice.AsyncResult __result); } Four additional overloads of are generated for use with and begin_getName generic completion callbacks type-safe .completion callbacksIce 3.4.2 Documentation 360 Copyright © 2011, ZeroC, Inc. As you can see, the single operation results in and methods. (The method is overloadedgetName begin_getName end_getName begin_ so you can pass a .)per-invocation context The method sends (or queues) an invocation of . This method does not block the calling thread.begin_getName getName The method collects the result of the asynchronous invocation. If, at the time the calling thread calls ,end_getName end_getName the result is not yet available, the calling thread blocks until the invocation completes. Otherwise, if the invocation completed some time before the call to , the method returns immediately with the result.end_getName A client could call these methods as follows: Java EmployeesPrx e = ...; Ice.AsyncResult r = e.begin_getName(99); // Continue to do other things here... String name = e.end_getName(r); Because does not block, the calling thread can do other things while the operation is in progress.begin_getName Note that returns a value of type . This value contains the state that the Ice run time requires to keep trackbegin_getName AsyncResult of the asynchronous invocation. You must pass the that is returned by the method to the corresponding AsyncResult begin_ end_ method. The method has one parameter for each in-parameter of the corresponding Slice operation. Similarly, the method has onebegin_ end_ out-parameter for each out-parameter of the corresponding Slice operation (plus the parameter). For example, consider theAsyncResult following operation: Slice double op(int inp1, string inp2, out bool outp1, out long outp2); The and methods have the following signature:begin_op end_op Java Ice.AsyncResult begin_op(int inp1, String inp2); Ice.AsyncResult begin_op(int inp1, String inp2, java.util.Map __ctx); double end_op(Ice.BooleanHolder outp1, Ice.LongHolder outp2, Ice.AsyncResult r); Asynchronous Exception Semantics in Java If an invocation raises an exception, the exception is thrown by the method, even if the actual error condition for the exception wasend_ encountered during the method ("on the way out"). The advantage of this behavior is that all exception handling is located with thebegin_ code that calls the method (instead of being present twice, once where the method is called, and again where the end_ begin_ end_ method is called). There is one exception to the above rule: if you destroy the communicator and then make an asynchronous invocation, the methodbegin_ throws . This is necessary because, once the run time is finalized, it can no longer throw anCommunicatorDestroyedException exception from the method.end_ The only other exception that is thrown by the and methods is . This exceptionbegin_ end_ java.lang.IllegalArgumentException indicates that you have used the API incorrectly. For example, the method throws this exception if you call an operation that has abegin_ return value or out-parameters on a oneway proxy. Similarly, the method throws this exception if you use a different proxy to call the end_ method than the proxy you used to call the method, or if the you pass to the method was obtained byend_ begin_ AsyncResult end_ calling the method for a different operation.begin_ AsyncResult Class in Java The that is returned by the method encapsulates the state of the asynchronous invocation:AsyncResult begin_Ice 3.4.2 Documentation 361 Copyright © 2011, ZeroC, Inc. Java public class AsyncResult { public Communicator getCommunicator(); public Connection getConnection(); public ObjectPrx getProxy(); public String getOperation(); public boolean isCompleted(); public void waitForCompleted(); public boolean isSent(); public void waitForSent(); public void throwLocalException(); public boolean sentSynchronously(); } The methods have the following semantics: Communicator getCommunicator() This method returns the communicator that sent the invocation. Connection getConnection() This method returns the connection that was used for the invocation. ObjectPrx getProxy() This method returns the proxy that was used to call the method.begin_ String getOperation() This method returns the name of the operation. boolean isCompleted() This method returns true if, at the time it is called, the result of an invocation is available, indicating that a call to the methodend_ will not block the caller. Otherwise, if the result is not yet available, the method returns false. void waitForCompleted() This method blocks the caller until the result of an invocation becomes available. boolean isSent() When you call the method, the Ice run time attempts to write the corresponding request to the client-side transport. If thebegin_ transport cannot accept the request, the Ice run time queues the request for later transmission. returns true if, at the time itisSent is called, the request has been written to the local transport (whether it was initially queued or not). Otherwise, if the request is still queued or an exception occurred before the request could be sent, returns false.isSent void waitForSent() This method blocks the calling thread until a request has been written to the client-side transport, or an exception occurs. After returns, returns true if the request was successfully written to the client-side transport, or false if anwaitForSent isSent exception occurred. In the case of a failure, you can call the corresponding method or to obtain theend_ throwLocalException exception. void throwLocalException() This method throws the local exception that caused the invocation to fail. If no exception has occurred yet, throwLocalException does nothing. boolean sentSynchronously() This method returns true if a request was written to the client-side transport without first being queued. If the request was initially queued, returns false (independent of whether the request is still in the queue or has since been written tosentSynchronously the client-side transport). Polling for Completion in Java The methods allow you to poll for call completion. Polling is useful in a variety of cases. As an example, consider theAsyncResult following simple interface to transfer files from client to server:Ice 3.4.2 Documentation 362 Copyright © 2011, ZeroC, Inc. Slice interface FileTransfer { void send(int offset, ByteSeq bytes); }; The client repeatedly calls to send a chunk of the file, indicating at which offset in the file the chunk belongs. A naïve way to transmit asend file would be along the following lines: Java FileHandle file = open(...); FileTransferPrx ft = ...; int chunkSize = ...; int offset = 0; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Read a chunk ft.send(offset, bs); // Send the chunk offset += bs.length; } This works, but not very well: because the client makes synchronous calls, it writes each chunk on the wire and then waits for the server to receive the data, process it, and return a reply before writing the next chunk. This means that both client and server spend much of their time doing nothing — the client does nothing while the server processes the data, and the server does nothing while it waits for the client to send the next chunk. Using asynchronous calls, we can improve on this considerably:Ice 3.4.2 Documentation 363 Copyright © 2011, ZeroC, Inc. Java FileHandle file = open(...); FileTransferPrx ft = ...; int chunkSize = ...; int offset = 0; LinkedList results = new LinkedList(); int numRequests = 5; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Send up to numRequests + 1 chunks asynchronously. Ice.AsyncResult r = ft.begin_send(offset, bs); offset += bs.length; // Wait until this request has been passed to the transport. r.waitForSent(); results.add(r); // Once there are more than numRequests, wait for the least // recent one to complete. while (results.size() > numRequests) { Ice.AsyncResult r = results.getFirst(); results.removeFirst(); r.waitForCompleted(); } } // Wait for any remaining requests to complete. while (results.size() > 0) { Ice.AsyncResult r = results.getFirst(); results.removeFirst(); r.waitForCompleted(); } With this code, the client sends up to chunks before it waits for the least recent one of these requests to complete. InnumRequests + 1 other words, the client sends the next request without waiting for the preceding request to complete, up to the limit set by . InnumRequests effect, this allows the client to "keep the pipe to the server full of data": the client keeps sending data, so both client and server continuously do work. Obviously, the correct chunk size and value of depend on the bandwidth of the network as well as the amount of time takennumRequests by the server to process each request. However, with a little testing, you can quickly zoom in on the point where making the requests larger or queuing more requests no longer improves performance. With this technique, you can realize the full bandwidth of the link to within a percent or two of the theoretical bandwidth limit of a native socket connection. Generic Completion Callbacks in Java The method is overloaded to allow you to provide completion callbacks. Here are the corresponding methods for the begin_ getName operation: Java Ice.AsyncResult begin_getName(int number, Ice.Callback __cb); Ice.AsyncResult begin_getName(int number, java.util.Map __ctx, Ice.Callback __cb); The second version of lets you override the default context. Following the in-parameters, the method accepts abegin_getName begin_Ice 3.4.2 Documentation 364 Copyright © 2011, ZeroC, Inc. parameter of type , which is a callback class with a method that you must provide. The Ice run time invokes the Ice.Callback completed method when an asynchronous operation completes. For example:completed Java public class MyCallback extends Ice.Callback { public void completed(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); try { String name = e.end_getName(r); System.out.println("Name is: " + name); } catch (Ice.LocalException ex) { System.err.println("Exception is: " + ex); } } } Note that your callback class must derive from . The implementation of your callback method must call the method.Ice.Callback end_ The proxy for the call is available via the method on the that is passed by the Ice run time. The return type of getProxy AsyncResult is , so you must down-cast the proxy to its correct type.getProxy Ice.ObjectPrx Your callback method should catch and handle any exceptions that may be thrown by the method. If an operation can throw userend_ exceptions, this means that you need an additional catch handler for (or catch all possible user exceptions explicitly).Ice.UserException If you allow an exception to escape from the callback method, the Ice run time produces a log entry by default and ignores the exception. (You can disable the log message by setting the property to zero.)Ice.Warn.AMICallback To inform the Ice run time that you want to receive a callback for the completion of the asynchronous call, you pass the callback instance to the method:begin_ Java EmployeesPrx e = ...; MyCallback cb = new MyCallback(); e.begin_getName(99, cb); This is often written using an anonymous class instead: Java EmployeesPrx e = ...; e.begin_getName( 99, new Ice.AsyncCallback() { public void completed(Ice.AsyncResult r) { EmployeesPrx p = (EmployeesPrx)r.getProxy(); try { String name = p.end_getName(r); System.out.println("Name is: " + name); } catch (Ice.LocalException ex) { System.err.println("Exception: " + ex); } } }); An anonymous class is useful particularly for callbacks that do only a small amount of work because the code that starts the call and the code that processes the results are physically close together.Ice 3.4.2 Documentation 365 Copyright © 2011, ZeroC, Inc. Sharing State Between and Methods in Javabegin_ end_ It is common for the method to require access to some state that is established by the code that calls the method. As anend_ begin_ example, consider an application that asynchronously starts a number of operations and, as each operation completes, needs to update different user interface elements with the results. In this case, the method knows which user interface element should receive thebegin_ update, and the method needs access to that element.end_ Assuming that we have a class that designates a particular user interface element, you could pass different widgets by storing theWidget widget to be used as a member of your callback class: Java public class MyCallback extends Ice.AsyncCallback { public MyCallback(Widget w) { _w = w; } private Widget _w; public void completed(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); try { String name = e.end_getName(r); _w.writeString(name); } catch (Ice.LocalException ex) { System.err.println("Exception is: " + ex); } } } For this example, we assume that widgets have a method that updates the relevant UI element.writeString When you call the method, you pass the appropriate callback instance to inform the method how to update the display:begin_ end_ Java EmployeesPrx e = ...; Widget widget1 = ...; Widget widget2 = ...; // Invoke the getName operation with different widget callbacks. e.begin_getName(99, new MyCallback(widget1)); e.begin_getName(24, new MyCallback(widget2)); The callback class provides a simple and effective way for you to pass state between the point where an operation is invoked and the point where its results are processed. Moreover, if you have a number of operations that share common state, you can pass the same callback instance to multiple invocations. (If you do this, your callback methods may need to use synchronization.) Type-Safe Completion Callbacks in Java The is not entirely type-safe:generic callback API You must down-cast the return value of to the correct proxy type before you can call the method.getProxy end_ You must call the correct method to match the operation called by the method.end_ begin_ You must remember to catch exceptions when you call the method; if you forget to do this, you will not know that the operationend_ failed. slice2java generates an additional type-safe API that takes care of these chores for you. To use type-safe callbacks, you must implement a callback class that provides two callback methods:Ice 3.4.2 Documentation 366 Copyright © 2011, ZeroC, Inc. a method that is called if the operation succeedsresponse an method that is called if the operation raises an exceptionexception Your callback class must derive from the base class that is generated by . The name of this base class is slice2java . Here is a callback class for an invocation of the operation:.Callback_ _ getName Java public class MyCallback extends Demo.Callback_Employees_getName { public void response(String name) { System.out.println("Name is: " + name); } public void exception(Ice.LocalException ex) { System.err.println("Exception is: " + ex); } } The callback parameters depend on the operation signature. If the operation has non- return type, the first parameter of the response void callback is the return value. The return value (if any) is followed by a parameter for each out-parameter of the correspondingresponse Slice operation, in the order of declaration. The callback is invoked if the invocation fails because of an Ice run time exception. If the Slice operation can also raise userexception exceptions, your callback class must supply an additional overloading of that accepts an argument of type exception .Ice.UserException The proxy methods are overloaded to accept this callback instance: Java Ice.AsyncResult begin_getName(int number, Callback_Employees_getName __cb); Ice.AsyncResult begin_getName(int number, java.util.Map __ctx, Callback_Employees_getName __cb); You pass the callback to an invocation as you would with the generic API: Java EmployeesPrx e = ...; MyCallback cb = new MyCallback(); e.begin_getName(99, cb); Asynchronous Oneway Invocations in Java You can invoke operations via oneway proxies asynchronously, provided the operation has return type, does not have anyvoid out-parameters, and does not raise user exceptions. If you call the method on a oneway proxy for an operation that returns valuesbegin_ or raises a user exception, the method throws an .begin_ IllegalArgumentException The callback methods looks exactly as for a twoway invocation. For the generic API, the Ice run time does not call the callbackcompleted method unless the invocation raised an exception during the method ("on the way out"). For the type-safe API, the begin_ response method is never called. Flow Control in JavaIce 3.4.2 Documentation 367 Copyright © 2011, ZeroC, Inc. Asynchronous method invocations never block the thread that calls the method: the Ice run time checks to see whether it can writebegin_ the request to the local transport. If it can, it does so immediately in the caller's thread. (In that case, AsyncResult.sentSynchronously returns true.) Alternatively, if the local transport does not have sufficient buffer space to accept the request, the Ice run time queues the request internally for later transmission in the background. (In that case, returns false.)AsyncResult.sentSynchronously This creates a potential problem: if a client sends many asynchronous requests at the time the server is too busy to keep up with them, the requests pile up in the client-side run time until, eventually, the client runs out of memory. The API provides a way for you to implement flow control by counting the number of requests that are queued so, if that number exceeds some threshold, the client stops invoking more operations until some of the queued operations have drained out of the local transport. For the , you can override the method:generic API sent Java public class MyCallback extends Ice.AsyncCallback { public void completed(Ice.AsyncResult r) { // ... } public void sent(Ice.AsyncResult r) { // ... } } You inform the Ice run time that you want to be informed when a call has been passed to the local transport as usual: Java e.begin_getName(99, new MyCallback()); If the Ice run time can immediately pass the request to the local transport, it does so and invokes the method from the thread that callssent the method. On the other hand, if the run time has to queue the request, it calls the method from a different thread once it hasbegin_ sent written the request to the local transport. In addition, you can find out from the that is returned by the methodAsyncResult begin_ whether the request was sent synchronously or was queued, by calling .sentSynchronously For the , the method has the following signature:generic API sent Java void sent(Ice.AsyncResult r); For the , the signature is:type-safe API Java void sent(boolean sentSynchronously); For the generic API, you can find out whether the request was sent synchronously by calling on the .sentSynchronously AsyncResult For the type-safe API, the boolean parameter provides the same information.sentSynchronously The methods allow you to limit the number of queued requests by counting the number of requests that are queued and decrementingsent the count when the Ice run time passes a request to the local transport. Asynchronous Batch Requests in Java Applications that send can either flush a batch explicitly or allow the Ice run time to flush automatically. The proxy method batched requests performs an immediate flush using the synchronous invocation model and may block the calling thread untilice_flushBatchRequests the entire message can be sent. Ice also provides asynchronous versions of this method so you can flush batch requests asynchronously.Ice 3.4.2 Documentation 368 Copyright © 2011, ZeroC, Inc. begin_ice_flushBatchRequests and are proxy methods that flush any batch requests queued byend_ice_flushBatchRequests that proxy. In addition, similar methods are available on the communicator and the object that is returned by Connection . These methods flush batch requests sent via the same communicator and via the same connection,AsyncResult.getConnection respectively. Concurrency Semantics for AMI in Java The Ice run time always invokes your callback methods from a separate thread, with one exception: it calls the callback from thesent thread calling the method if the request could be sent synchronously. In the callback, you know which thread is calling thebegin_ sent callback by looking at the member or parameter.sentSynchronously AMI Limitations in Java AMI invocations cannot be sent using collocated optimization. If you attempt to invoke an AMI operation using a proxy that is configured to use , the Ice run time raises if the servant happens to be collocated; thecollocation optimization CollocationOptimizationException request is sent normally if the servant is not collocated. You can disable this optimization if necessary. See Also Request Contexts Batched Invocations Location TransparencyIce 3.4.2 Documentation 369 Copyright © 2011, ZeroC, Inc. Using the Slice Compiler for Java On this page: Command-Line Optionsslice2java Slice2Java Ant Task Execution Environment Dependencies Parameters Nested Elements Using the Task slice2java Command-Line Options The Slice-to-Java compiler, , offers the following command-line options in addition to the :slice2java standard options --tie Generate .tie classes --impl Generate sample implementation files. This option will not overwrite an existing file. --impl-tie Generate sample implementation files using . This option will not overwrite an existing file.tie classes --checksum CLASS Generate for Slice definitions into the class . The given class name may optionally contain a package specifier.checksums CLASS The generated class contains checksums for all of the Slice files being translated by this invocation of the compiler. For example, the command below causes to generate the file containing the checksums for the Slice definitionsslice2java Checksums.java in and : File1.ice File2.ice slice2java --checksum Checksums File1.ice File2.ice --stream Generate for Slice types.streaming helper functions --meta META Define the global metadata directive . Using this option is equivalent to defining the global metadata in each named SliceMETA META file, as well as in any file included by a named Slice file. Global metadata specified with overrides any corresponding global--meta metadata directive in the files being compiled. Slice2Java Ant Task The Ice for Java build system makes extensive use of an ant task named to automate the execution of the Slice-to-JavaSlice2JavaTask compiler. This task may also be useful for Ice developers. The task and its supporting classes reside in the JAR file named ,ant-ice.jar which normally can be found in the subdirectory of your Ice installation.lib Execution Environment The must be able to locate and spawn the executable. You can specify the directory of your Ice installationSlice2JavaTask slice2java by defining the ant property or the environment variable, in which case the task assumes that the Slice compiler'sice.home ICE_HOME executable is located in the subdirectory of the specified installation directory. For example, if is set to on Linux,bin ICE_HOME /opt/Ice the task assumes that the executable path name is . Furthermore, the task also configures its shared library/opt/Ice/bin/slice2java search path (if necessary for your platform) to ensure the executable can resolve its library dependencies. If both and are defined, takes precedence. If neither are defined, the task assumes that the executableice.home ICE_HOME ice.home can already be found in your and that your shared library search path is configured correctly.PATH Finally, you can use a task parameter to specify the full path name of the Slice compiler. Again, the task assumes that your shared library search path is configured correctly. DependenciesIce 3.4.2 Documentation 370 Copyright © 2011, ZeroC, Inc. The task minimizes recompilation by maintaining dependencies between Slice files. The task stores this information in a file named .depend in the output directory and updates these dependencies after each invocation. (You can specify a different name for this file using a task parameter.) Note that the task does not maintain dependencies between a Slice file and its generated Java source files. Consequently, removing the generated Java source files does not cause the task to recompile a Slice file. In fact, the task only compiles a Slice file when any of the following conditions are true: no dependency file exists no dependency information is found for the Slice file the modification time of the Slice file is later than the modification time of the dependency file the Slice file includes another Slice file that is eligible for compilation The simplest way to force the task to recompile all of your Slice files is to remove the dependency file. Parameters The task supports the parameters listed in the following table: Attribute Description Required checksum Specifies the name of a class to contain the .Slice checksums No dependencyfile Specifies an alternate name for the dependency file. If you specify a relative filename, it is relative to ant's current working directory. If not specified, the task uses the name by default. If you do not.depend define this attribute and is defined, the task creates the file in the designatedoutputdir .depend output directory (see ).outputdir No ice Instructs the Slice compiler to permit symbols that have the reserved prefix . This parameter is usedIce in the Ice build system and is not normally required by applications. No outputdir Specifies the directory in which the Slice compiler generates Java source files. If not specified, the task uses ant's current working directory. No stream Indicates whether to generate . If not specified, streaming support is not generated.streaming support No tie Indicates whether to generate . If not specified, tie classes are not generated.tie classes No translator Specifies the path name of the Slice compiler. If not specified, the task locates the Slice compiler in its .execution environment No For the flag parameters ( , , and ), legal positive values are , , or ; negative values are , , or .ice stream tie on true yes off false no Nested Elements Several Slice compiler options must be defined as nested elements of the task: define Defines a preprocessor macro. The element supports the attributes and (optionally) , as shown below:name value These definitions are equivalent to the command-line options and , respectively.-DFOO -DBAR=5 fileset Specifies the set of Slice files to be compiled. Refer to the ant documentation of its type for more information.FileSet includepath Specifies the include file search path for Slice files. In ant terminology, is a . Refer to the antincludepath path-like structure documentation of its type for more information.Path meta Defines a global metadata directive in each Slice file as well as in each included Slice file. The element supports and name value attributes. Using the Task Define the following element in your project's build file to enable the task:taskdefIce 3.4.2 Documentation 371 Copyright © 2011, ZeroC, Inc. Ant This configuration assumes that is already present in ant's class path. Alternatively, you can specify the JAR explicitly asant-ice.jar follows: Ant Once activated, you can invoke the task to translate your Slice files. The example shown below is a simplified version of the ant project for the demo:hello Ant ... This project demonstrates some practices that we encourage you to adopt in your own projects. First, it is helpful to keep the source files generated by the Slice compiler separate from your application's source files by dedicating an output directory for the exclusive use of the Slice compiler. Doing so helps to minimize confusion and makes it easier to configure a source-code management system to ignore generated files. Next, we also recommend that you include a target in your ant project that removes this output directory. Assuming that theclean dependency file ( ) is also stored in this directory, removing the output directory is an efficient way to clean up your project's source.depend tree and guarantees that all of your Slice files are recompiled in the next build. Finally, after seeing the element in the invocation of you might infer that the generated code was not being compiled, butexclude javac the presence of the output directory in the attribute ensures that the generated code is included in the build. The purpose of the srcdir element is to prevent ant from including the generated files twice in its target list.exclude See Also Using the Slice Compilers Using Slice Checksums in Java Tie Classes in Java Streaming InterfacesIce 3.4.2 Documentation 372 Copyright © 2011, ZeroC, Inc. Using Slice Checksums in Java The Slice compilers can optionally generate of Slice definitions. For , the option causes the compilerchecksums slice2java --checksum to generate a new Java class that adds checksums to a static map member. Assuming we supplied the option to --checksum Checksums , the generated class looks like this:slice2java Checksums.java Java public class Checksums { public static java.util.Map checksums; } The read-only map is initialized automatically prior to first use; no action is required by the application.checksums In order to verify a server's checksums, a client could simply compare the dictionaries using the method. However, this is notequals feasible if it is possible that the server might return a superset of the client's checksums. A more general solution is to iterate over the local checksums as demonstrated below: Java java.util.Map serverChecksums = ... java.util.Iterator> i = Checksums.checksums.entrySet().iterator(); while(i.hasNext()) { java.util.Map.Entry e = i.next(); String id = e.getKey(); String checksum = e.getValue(); String serverChecksum = serverChecksums.get(id); if (serverChecksum == null) { // No match found for type id! } else if (!checksum.equals(serverChecksum)) { // Checksum mismatch! } } In this example, the client first verifies that the server's dictionary contains an entry for each Slice type ID, and then it proceeds to compare the checksums. See Also Slice ChecksumsIce 3.4.2 Documentation 373 Copyright © 2011, ZeroC, Inc. Example of a File System Client in Java This page presents the source code for a very simple client to access a server that implements the file system we developed in Slice for a . The Java code hardly differs from the code you would write for an ordinary Java program. This is one of the biggestSimple File System advantages of using Ice: accessing a remote object is as easy as accessing an ordinary, local Java object. This allows you to put your effort where you should, namely, into developing your application logic instead of having to struggle with arcane networking APIs. This is true for the as well, meaning that you can develop distributed applications easily and efficiently.server side We now have seen enough of the client-side Java mapping to develop a complete client to access our remote file system. For reference, here is the Slice definition once more: Slice module Filesystem { interface Node { idempotent string name(); }; exception GenericError { string reason; }; sequence Lines; interface File extends Node { idempotent Lines read(); idempotent void write(Lines text) throws GenericError; }; sequence NodeSeq; interface Directory extends Node { idempotent NodeSeq list(); }; }; To exercise the file system, the client does a recursive listing of the file system, starting at the root directory. For each node in the file system, the client shows the name of the node and whether that node is a file or directory. If the node is a file, the client retrieves the contents of the file and prints them. The body of the client code looks as follows: Java import Filesystem.*; public class Client { // Recursively print the contents of directory "dir" in // tree fashion. For files, show the contents of each file. // The "depth" parameter is the current nesting level // (for indentation). static void listRecursive(DirectoryPrx dir, int depth) { char[] indentCh = new char[++depth]; java.util.Arrays.fill(indentCh, '\t'); String indent = new String(indentCh); NodePrx[] contents = dir.list(); for (int i = 0; i < contents.length; ++i) {Ice 3.4.2 Documentation 374 Copyright © 2011, ZeroC, Inc. DirectoryPrx subdir = DirectoryPrxHelper.checkedCast(contents[i]); FilePrx file = FilePrxHelper.uncheckedCast(contents[i]); System.out.println(indent + contents[i].name() + (subdir != null ? " (directory):" : " (file):")); if (subdir != null) { listRecursive(subdir, depth); } else { String[] text = file.read(); for (int j = 0; j < text.length; ++j) System.out.println(indent + "\t" + text[j]); } } } public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { // Create a communicator // ic = Ice.Util.initialize(args); // Create a proxy for the root directory // Ice.ObjectPrx base = ic.stringToProxy("RootDir:default -p 10000"); if (base == null) throw new RuntimeException("Cannot create proxy"); // Down-cast the proxy to a Directory proxy // DirectoryPrx rootDir = DirectoryPrxHelper.checkedCast(base); if (rootDir == null) throw new RuntimeException("Invalid proxy"); // Recursively list the contents of the root directory // System.out.println("Contents of root directory:"); listRecursive(rootDir, 0); } catch (Ice.LocalException e) { e.printStackTrace(); status = 1; } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { System.err.println(e.getMessage()); status = 1; } } System.exit(status);Ice 3.4.2 Documentation 375 Copyright © 2011, ZeroC, Inc. 1. 2. 1. 2. 3. } } After importing the package, the class defines two methods: , which is a helper function to print theFilesystem Client listRecursive contents of the file system, and , which is the main program. Let us look at first:main main The structure of the code in follows what we saw in . After initializing the run time, the client creates amain Hello World Application proxy to the root directory of the file system. For this example, we assume that the server runs on the local host and listens using the default protocol (TCP/IP) at port 10000. The object identity of the root directory is known to be .RootDir The client down-casts the proxy to and passes that proxy to , which prints the contents of the fileDirectoryPrx listRecursive system. Most of the work happens in . The function is passed a proxy to a directory to list, and an indent level. (The indent levellistRecursive increments with each recursive call and allows the code to print the name of each node at an indent level that corresponds to the depth of the tree at that node.) calls the operation on the directory and iterates over the returned sequence of nodes:listRecursive list The code does a to narrow the proxy to a proxy, as well as an to narrow the checkedCast Node Directory uncheckedCast proxy to a proxy. Exactly one of those casts will succeed, so there is no need to call twice: if the Node File checkedCast Node , the code uses the returned by the ; if the fails, we that theis-a Directory DirectoryPrx checkedCast checkedCast know Node File and, therefore, an is sufficient to get a . is-a uncheckedCast FilePrx In general, if you know that a down-cast to a specific type will succeed, it is preferable to use an instead of a uncheckedCast because an does not incur any network traffic.checkedCast uncheckedCast The code prints the name of the file or directory and then, depending on which cast succeeded, prints or "(directory)" following the name."(file)" The code checks the type of the node: If it is a directory, the code recurses, incrementing the indent level. If it is a file, the code calls the operation on the file to retrieve the file contents and then iterates over the returnedread sequence of lines, printing each line. Assume that we have a small file system consisting of two files and a directory as follows: A small file system. The output produced by the client for this file system is: Contents of root directory: README (file): This file system contains a collection of poetry. Coleridge (directory): Kubla_Khan (file): In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea.Ice 3.4.2 Documentation 376 Copyright © 2011, ZeroC, Inc. Note that, so far, our client (and server) are not very sophisticated: The protocol and address information are hard-wired into the code. The client makes more remote procedure calls than strictly necessary; with minor redesign of the Slice definitions, many of these calls can be avoided. We will see how to address these shortcomings in our discussions of and .IceGrid object life cycle See Also Hello World Application Slice for a Simple File System Example of a File System Server in Java Object Life Cycle IceGridIce 3.4.2 Documentation 377 Copyright © 2011, ZeroC, Inc. Server-Side Slice-to-Java Mapping The mapping for Slice data types to Java is identical on the client side and server side. This means that everything in Client-Side also applies to the server side. However, for the server side, there are a few additional things you need to know —Slice-to-Java Mapping specifically how to: Initialize and finalize the server-side run time Implement servants Pass parameters and throw exceptions Create servants and register them with the Ice run time Because the mapping for Slice data types is identical for clients and servers, the server-side mapping only adds a few additional mechanisms to the client side: a small API to initialize and finalize the run time, plus a few rules for how to derive servant classes from skeletons and how to register servants with the server-side run time. Although the examples we present are very simple, they accurately reflect the basics of writing an Ice server. Of course, for more sophisticated servers, you will be using , for example, to improve performance or scalability. However, these APIs are alladditional APIs described in Slice, so, to use these APIs, you need not learn any Java mapping rules beyond those we described here. Topics The Server-Side main Method in Java Server-Side Java Mapping for Interfaces Parameter Passing in Java Raising Exceptions in Java Tie Classes in Java Object Incarnation in Java Asynchronous Method Dispatch (AMD) in Java Example of a File System Server in JavaIce 3.4.2 Documentation 378 Copyright © 2011, ZeroC, Inc. The Server-Side main Method in Java On this page: A Basic Method in Javamain The Class in JavaIce.Application Using on the Client Side in JavaIce.Application Catching Signals in Java and Properties in JavaIce.Application Limitations of in JavaIce.Application A Basic Method in Javamain The main entry point to the Ice run time is represented by the local Slice interface . As for the client side, you mustIce::Communicator initialize the Ice run time by calling before you can do anything else in your server. Ice.Util.initialize Ice.Util.initialize returns a reference to an instance of an :Ice.Communicator Java public class Server { public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(args); // ... } catch (Exception e) { e.printStackTrace(); status = 1; } // ... } } Ice.Util.initialize accepts the argument vector that is passed to by the operating system. The function scans the argumentmain vector for any that are relevant to the Ice run time, but does not remove those options. If anything goes wrong duringcommand-line options initialization, throws an exception.initialize The semantics of Java arrays prevents from modifying the size of the argument vector.Ice.Util.initialize However, of is provided that allows the application to obtain a newanother overloading Ice.Util.initialize argument vector with the Ice options removed. Before leaving your function, you call . The operation is responsible for finalizing the Ice runmain must Communicator.destroy destroy time. In particular, waits for any operation implementations that are still executing in the server to complete. In addition, destroy destroy ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as file descriptors and memory. Never allow your function to terminate without calling first; doing so has undefined behavior.main destroy The general shape of our server-side function is therefore as follows:mainIce 3.4.2 Documentation 379 Copyright © 2011, ZeroC, Inc. Java public class Server { public static void main(String[] args) { int status = 0; Ice.Communicator ic = null; try { ic = Ice.Util.initialize(args); // ... } catch (Exception e) { e.printStackTrace(); status = 1; } if (ic != null) { try { ic.destroy(); } catch (Exception e) { e.printStackTrace(); status = 1; } } System.exit(status); } } Note that the code places the call to into a block and takes care to return the correct exit status to theIce.Util.initialize try operating system. Also note that an attempt to destroy the communicator is made only if the initialization succeeded. The Class in JavaIce.Application The preceding structure for the function is so common that Ice offers a class, , that encapsulates all the correctmain Ice.Application initialization and finalization activities. The synopsis of the class is as follows (with some detail omitted for now): Java package Ice; public enum SignalPolicy { HandleSignals, NoSignalHandling } public abstract class Application { public Application() public Application(SignalPolicy signalPolicy) public final int main(String appName, String[] args) public final int main(String appName, String[] args, String configFile) public final int main(String appName, String[] args, InitializationData initData) public abstract int run(String[] args) public static String appName() public static Communicator communicator() // ... } The intent of this class is that you specialize and implement the abstract method in your derived class. WhateverIce.Application runIce 3.4.2 Documentation 380 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. code you would normally place in goes into the method instead. Using , our program looks as follows:main run Ice.Application Java public class Server extends Ice.Application { public int run(String[] args) { // Server code here... return 0; } public static void main(String[] args) { Server app = new Server(); int status = app.main("Server", args); System.exit(status); } } Note that is overloaded: you can pass an optional file name or an structure.Application.main InitializationData If you pass a to , the property settings in this file are overridden by settings in a file identified by the configuration file name main environment variable (if defined). Property settings supplied on the take precedence over all other settings.ICE_CONFIG command line The function does the following:Application.main It installs an exception handler for . If your code fails to handle an exception, printsjava.lang.Exception Application.main the name of an exception and a stack trace on before returning with a non-zero return value.System.err It initializes (by calling ) and finalizes (by calling ) a communicator. You can getIce.Util.initialize Communicator.destroy access to the communicator for your server by calling the static accessor.communicator It scans the argument vector for options that are relevant to the Ice run time and removes any such options. The argument vector that is passed to your method therefore is free of Ice-related options and only contains options and arguments that are specificrun to your application. It provides the name of your application via the static member function. The return value from this call is the first argumentappName in the call to , so you can get at this name from anywhere in your code by calling Application.main (which is usually required for error messages). In the example above, the return value from Ice.Application.appName would be .appName Server It installs a shutdown hook that properly shuts down the communicator. It installs a if the application has not already configured one. The per-process logger uses the value of the per-process logger property as a prefix for its messages and sends its output to the standard error channel. An application canIce.ProgramName also specify an .alternate logger Using ensures that your program properly finalizes the Ice run time, whether your server terminates normally or inIce.Application response to an exception. We recommend that all your programs use this class; doing so makes your life easier. In addition, also provides features for signal handling and configuration that you do not have to implement yourself when you useIce.Application this class. Using on the Client Side in JavaIce.Application You can use for your clients as well: simply implement a class that derives from and place theIce.Application Ice.Application client code into its method. The advantage of this approach is the same as for the server side: ensures that therun Ice.Application communicator is destroyed correctly even in the presence of exceptions. Catching Signals in Java The simple server we developed in had no way to shut down cleanly: we simply interrupted the server from theHello World Application command line to force it to exit. Terminating a server in this fashion is unacceptable for many real-life server applications: typically, the server has to perform some cleanup work before terminating, such as flushing database buffers or closing network connections. This is particularly important on receipt of a signal or keyboard interrupt to prevent possible corruption of database files or other persistent data. Java does not provide direct support for signals, but it does allow an application to register a that is invoked when the JVM isshutdown hook shutting down. There are several events that trigger JVM shutdown, such as a call to or an interrupt signal from the operatingSystem.exit system, but the shutdown hook is not provided with the reason for the shut down.Ice 3.4.2 Documentation 381 Copyright © 2011, ZeroC, Inc. Ice.Application registers a shutdown hook by default, allowing you to cleanly terminate your application prior to JVM shutdown. Java package Ice; public abstract class Application { // ... synchronized public static void destroyOnInterrupt() synchronized public static void shutdownOnInterrupt() synchronized public static void setInterruptHook(Thread t) synchronized public static void defaultInterrupt() synchronized public static boolean interrupted() } The functions behave as follows: destroyOnInterrupt This function installs a shutdown hook that calls on the communicator. This is the default behavior.destroy shutdownOnInterrupt This function installs a shutdown hook that calls on the communicator.shutdown setInterruptHook This function installs a custom shutdown hook that takes responsibility for performing whatever action is necessary to terminate the application. Refer to the Java documentation for for more information on the semantics of shutdownRuntime.addShutdownHook hooks. defaultInterrupt This function removes the shutdown hook. interrupted This function returns true if the shutdown hook caused the communicator to shut down, false otherwise. This allows us to distinguish intentional shutdown from a forced shutdown that was caused by the JVM. This is useful, for example, for logging purposes. By default, behaves as if was invoked, therefore our server function requires no changeIce.Application destroyOnInterrupt main to ensure that the program terminates cleanly on JVM shutdown. (You can disable this default shutdown hook by passing the enumerator to the constructor. In that case, shutdown is not intercepted and terminates the VM.) However, we add a diagnostic toNoSignalHandling report the occurrence, so our function now looks like:main Java public class Server extends Ice.Application { public int run(String[] args) { // Server code here... if (interrupted()) System.err.println(appName() + ": terminating"); return 0; } public static void main(String[] args) { Server app = new Server(); int status = app.main("Server", args); System.exit(status); } } During the course of normal execution, the JVM does not terminate until all non-daemon threads have completed. If an interrupt occurs, theIce 3.4.2 Documentation 382 Copyright © 2011, ZeroC, Inc. JVM ignores the status of active threads and terminates as soon as it has finished invoking all of the installed shutdown hooks. In a subclass of , the default shutdown hook (as installed by ) blocks until the application's mainIce.Application destroyOnInterrupt thread completes. As a result, an interrupted application may not terminate successfully if the main thread is blocked. For example, this can occur in an interactive application when the main thread is waiting for console input. To remedy this situation, the application can install an alternate shutdown hook that does not wait for the main thread to finish: Java public class Server extends Ice.Application { class ShutdownHook extends Thread { public void run() { try { communicator().destroy(); } catch(Ice.LocalException ex) { ex.printStackTrace(); } } } public int run(String[] args) { setInterruptHook(new ShutdownHook()); // ... } } After replacing the default shutdown hook using , the JVM will terminate as soon as the communicator is destroyed.setInterruptHook Ice.Application and Properties in Java Apart from the functionality shown in this section, also takes care of initializing the Ice run time with property values. Ice.Application allow you to configure the run time in various ways. For example, you can use properties to control things such as the thread poolProperties size or port number for a server. The function of is overloaded; the second version allows you to specify the namemain Ice.Application of a configuration file that will be processed during initialization. Limitations of in JavaIce.Application Ice.Application is a singleton class that creates a single communicator. If you are using multiple communicators, you cannot use . Instead, you must structure your code as we saw in (taking care to always destroy theIce.Application Hello World Application communicator). See Also Hello World Application Properties and Configuration Communicator Initialization Logger FacilityIce 3.4.2 Documentation 383 Copyright © 2011, ZeroC, Inc. Server-Side Java Mapping for Interfaces The server-side mapping for interfaces provides an up-call API for the Ice run time: by implementing member functions in a servant class, you provide the hook that gets the thread of control from the Ice server-side run time into your application code. On this page: Skeleton Classes in Java Servant Classes in Java Normal and Operations in Javaidempotent Skeleton Classes in Java On the client side, interfaces map to . On the server side, interfaces map to classes. A skeleton is a class that has aproxy classes skeleton pure virtual member function for each operation on the corresponding interface. For example, consider our for the Slice definition Node interface: Slice module Filesystem { interface Node { idempotent string name(); }; // ... }; The Slice compiler generates the following definition for this interface: Java package Filesystem; public interface _NodeOperations { String name(Ice.Current current); } public interface _NodeOperationsNC { String name(); } public interface Node extends Ice.Object, _NodeOperations, _NodeOperationsNC {} public abstract class _NodeDisp extends Ice.ObjectImpl implements Node { // Mapping-internal code here... } The important points to note here are: As for the client side, Slice modules are mapped to Java packages with the same name, so the skeleton class definitions are part of the package.Filesystem For each Slice interface , the compiler generates Java interfaces and _ Operations _ ( and in this example). These interfaces contain aOperationsNC _NodeOperations _NodeOperationsNC method for each operation in the Slice interface. (You can ignore the parameter for now.)Ice.Current For each Slice interface , the compiler generates a Java interface ( in this NodeIce 3.4.2 Documentation 384 Copyright © 2011, ZeroC, Inc. example). That interface extends and the two operations interfaces.Ice.Object For each Slice interface , the compiler generates an abstract class ( _ Disp _NodeDisp in this example). This abstract class is the actual skeleton class; it is the base class from which you derive your servant class. Servant Classes in Java In order to provide an implementation for an Ice object, you must create a servant class that inherits from the corresponding skeleton class. For example, to create a servant for the interface, you could write:Node Java package Filesystem; public final class NodeI extends _NodeDisp { public NodeI(String name) { _name = name; } public String name(Ice.Current current) { return _name; } private String _name; } By convention, servant classes have the name of their interface with an -suffix, so the servant class for the interface is called .I Node NodeI (This is a convention only: as far as the Ice run time is concerned, you can choose any name you prefer for your servant classes.) Note that extends , that is, it derives from its skeleton class.NodeI _NodeDisp As far as Ice is concerned, the class must implement only a single method: the method that it inherits from its skeleton. ThisNodeI name makes the servant class a concrete class that can be instantiated. You can add other member functions and data members as you see fit to support your implementation. For example, in the preceding definition, we added a member and a constructor. (Obviously, the_name constructor initializes the member and the function returns its value.)_name name Normal and Operations in Javaidempotent Whether an operation is an ordinary operation or an operation has no influence on the way the operation is mapped. Toidempotent illustrate this, consider the following interface: Slice interface Example { void normalOp(); idempotent void idempotentOp(); idempotent string readonlyOp(); }; The operations class for this interface looks like this: Java public interface _ExampleOperations { void normalOp(Ice.Current current); void idempotentOp(Ice.Current current); String readonlyOp(Ice.Current current); }Ice 3.4.2 Documentation 385 Copyright © 2011, ZeroC, Inc. Note that the signatures of the member functions are unaffected by the qualifier.idempotent See Also Slice for a Simple File System Java Mapping for Interfaces Parameter Passing in Java Raising Exceptions in Java Tie Classes in Java The Current ObjectIce 3.4.2 Documentation 386 Copyright © 2011, ZeroC, Inc. Parameter Passing in Java For each parameter of a Slice operation, the Java mapping generates a corresponding parameter for the method in the interface. In addition, every operation has an additional, trailing parameter of type . For_ Operations Ice.Current example, the operation of the interface has no parameters, but the method of the interface has aname Node name _NodeOperations single parameter of type . We will ignore this parameter for now.Ice.Current To illustrate the rules, consider the following interface that passes string parameters in all possible directions: Slice module M { interface Example { string op(string sin, out string sout); }; }; The generated skeleton class for this interface looks as follows: Java public interface _ExampleOperations { String op(String sin, Ice.StringHolder sout, Ice.Current current); } As you can see, there are no surprises here. For example, we could implement as follows:op Java public final class ExampleI extends M._ExampleDisp { public String op(String sin, Ice.StringHolder sout, Ice.Current current) { System.out.println(sin); // In params are initialized sout.value = "Hello World!"; // Assign out param return "Done"; } } This code is in no way different from what you would normally write if you were to pass strings to and from a function; the fact that remote procedure calls are involved does not impact on your code in any way. The same is true for parameters of other types, such as proxies, classes, or dictionaries: the parameter passing conventions follow normal Java rules and do not require special-purpose API calls. See Also Server-Side Java Mapping for Interfaces Raising Exceptions in Java Tie Classes in Java The Current ObjectIce 3.4.2 Documentation 387 Copyright © 2011, ZeroC, Inc. Raising Exceptions in Java To throw an exception from an operation implementation, you simply instantiate the exception, initialize it, and throw it. For example: Java // ... public void write(String[] text, Ice.Current current) throws GenericError { try { // Try to write file contents here... } catch(Exception ex) { throw new GenericError("Exception during write operation", ex); } } Note that, for this example, we have supplied the to the constructor. This parameter sets theoptional second parameter GenericError inner exception and preserves the original cause of the error for later diagnosis. If you throw an arbitrary Java run-time exception (such as a ), the Ice run time catches the exception and thenClassCastException returns an to the client. Similarly, if you throw an "impossible" user exception (a user exception that is not listed in theUnknownException exception specification of the operation), the client receives an .UnknownUserException If you throw an Ice run-time exception, such as , the client receives an . For thatMemoryLimitException UnknownLocalException reason, you should never throw system exceptions from operation implementations. If you do, all the client will see is an , which does not tell the client anything useful.UnknownLocalException Three run-time exceptions are and not changed to when returned to thetreated specially UnknownLocalException client: , , and .ObjectNotExistException OperationNotExistException FacetNotExistException See Also Run-Time Exceptions Java Mapping for Exceptions Server-Side Java Mapping for Interfaces Parameter Passing in Java Tie Classes in JavaIce 3.4.2 Documentation 388 Copyright © 2011, ZeroC, Inc. Tie Classes in Java The mapping to requires the servant class to inherit from its skeleton class. Occasionally, this creates a problem: someskeleton classes class libraries require you to inherit from a base class in order to access functionality provided by the library; because Java does not support multiple implementation inheritance, this means that you cannot use such a class library to implement your servants because your servants cannot inherit from both the library class and the skeleton class simultaneously. To allow you to still use such class libraries, Ice provides a way to write servants that replaces inheritance with delegation. This approach is supported by . The idea is that, instead of inheriting from the skeleton class, you simply create a class (known as an tie classes or ) that contains methods corresponding to the operations of an interface. You use the optionimplementation class delegate class --tie with the compiler to create a tie class. For example, the option causes the compiler to create exactly the same code forslice2java --tie the as we saw previously, but to also emit an additional tie class. For an interface , the generated tie interfaceNode class has the name :_ TieIce 3.4.2 Documentation 389 Copyright © 2011, ZeroC, Inc. Java package Filesystem; public class _NodeTie extends _NodeDisp implements Ice.TieBase { public _NodeTie() {} public _NodeTie(_NodeOperations delegate) { _ice_delegate = delegate; } public java.lang.Object ice_delegate() { return _ice_delegate; } public void ice_delegate(java.lang.Object delegate) { _ice_delegate = (_NodeOperations)delegate; } public boolean equals(java.lang.Object rhs) { if (this == rhs) { return true; } if (!(rhs instanceof _NodeTie)) { return false; } return _ice_delegate.equals(((_NodeTie)rhs)._ice_delegate); } public int hashCode() { return _ice_delegate.hashCode(); } public String name(Ice.Current current) { return _ice_delegate.name(current); } private _NodeOperations _ice_delegate; } This looks a lot worse than it is: in essence, the generated tie class is simply a servant class (it extends ) that delegates to your_NodeDisp implementation class each invocation of a method corresponding to a Slice operation:Ice 3.4.2 Documentation 390 Copyright © 2011, ZeroC, Inc. A skeleton class, tie class, and implementation class. The generated tie class also implements the interface, which defines methods for obtaining and changing the delegateIce.TieBase object: Java package Ice; public interface TieBase { java.lang.Object ice_delegate(); void ice_delegate(java.lang.Object delegate); } The delegate has type in these methods in order to allow a tie object's delegate to be manipulated without knowing itsjava.lang.Object actual type. However, the modifier raises if the given delegate object is not of the correct type.ice_delegate ClassCastException Given this machinery, we can create an implementation class for our interface as follows:Node Java package Filesystem; public final class NodeI implements _NodeOperations { public NodeI(String name) { _name = name; } public String name(Ice.Current current) { return _name; } private String _name; } Note that this class is identical to our previous implementation, except that it implements the interface and does not_NodeOperations extend (which means that you are now free to extend any other class to support your implementation)._NodeDisp To create a servant, you instantiate your implementation class and the tie class, passing a reference to the implementation instance to the tie constructor: Java NodeI fred = new NodeI("Fred"); // Create implementation _NodeTie servant = new _NodeTie(fred); // Create tie Alternatively, you can also default-construct the tie class and later set its delegate instance by calling :ice_delegateIce 3.4.2 Documentation 391 Copyright © 2011, ZeroC, Inc. Java _NodeTie servant = new _NodeTie(); // Create tie // ... NodeI fred = new NodeI("Fred"); // Create implementation // ... servant.ice_delegate(fred); // Set delegate When using tie classes, it is important to remember that the tie instance is the servant, not your delegate. Furthermore, you must not use a tie instance to an Ice object until the tie has a delegate. Once you have set the delegate, you must not change it for the lifetime ofincarnate the tie; otherwise, undefined behavior results. You should use the tie approach only when necessary, that is, if you need to extend some base class in order to implement your servants: using the tie approach is more costly in terms of memory because each Ice object is incarnated by two Java objects (the tie and the delegate) instead of just one. In addition, call dispatch for ties is marginally slower than for ordinary servants because the tie forwards each operation to the delegate, that is, each operation invocation requires two function calls instead of one. Also note that, unless you arrange for it, there is no way to get from the delegate back to the tie. If you need to navigate back to the tie from the delegate, you can store a reference to the tie in a member of the delegate. (The reference can, for example, be initialized by the constructor of the delegate.) See Also Server-Side Java Mapping for Interfaces Parameter Passing in Java Raising Exceptions in Java Object Incarnation in JavaIce 3.4.2 Documentation 392 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. Object Incarnation in Java Having created a servant class such as the rudimentary , you can instantiate the class to create a concrete servant that can classNodeI receive invocations from a client. However, merely instantiating a servant class is insufficient to incarnate an object. Specifically, to provide an implementation of an Ice object, you must take the following steps: Instantiate a servant class. Create an identity for the Ice object incarnated by the servant. Inform the Ice run time of the existence of the servant. Pass a proxy for the object to a client so the client can reach it. On this page: Instantiating a Java Servant Creating an Identity in Java Activating a Java Servant UUIDs as Identities in Java Creating Proxies in Java Proxies and Servant Activation in Java Direct Proxy Creation in Java Instantiating a Java Servant Instantiating a servant means to allocate an instance: Java Node servant = new NodeI("Fred"); This code creates a new instance and assigns its address to a reference of type . This works because is derived from NodeI Node NodeI , so a reference can refer to an instance of type . However, if we want to invoke a member function of the class atNode Node NodeI NodeI this point, we must use a reference:NodeI Java NodeI servant = new NodeI("Fred"); Whether you use a or a reference depends purely on whether you want to invoke a member function of the class: if not,Node NodeI NodeI a reference works just as well as a reference.Node NodeI Creating an Identity in Java Each Ice object requires an identity. That identity must be unique for all servants using the same object adapter. The Ice object model assumes that all objects (regardless of their adapter) have a .globally unique identity An Ice object identity is a structure with the following Slice definition: Slice module Ice { struct Identity { string name; string category; }; // ... };Ice 3.4.2 Documentation 393 Copyright © 2011, ZeroC, Inc. 1. 2. 3. The full identity of an object is the combination of both the and fields of the structure. For now, we will leave the name category Identity field as the empty string and simply use the field. (The field is most often used in conjunction with category name category servant .)locators To create an identity, we simply assign a key that identifies the servant to the field of the structure:name Identity Java Ice.Identity id = new Ice.Identity(); id.name = "Fred"; // Not unique, but good enough for now Activating a Java Servant Merely creating a servant instance does nothing: the Ice run time becomes aware of the existence of a servant only once you explicitly tell the object adapter about the servant. To activate a servant, you invoke the operation on the object adapter. Assuming that we haveadd access to the object adapter in the variable, we can write:_adapter Java _adapter.add(servant, id); Note the two arguments to : the servant and the object identity. Calling on the object adapter adds the servant and the servant'sadd add identity to the adapter's servant map and links the proxy for an Ice object to the correct servant instance in the server's memory as follows: The proxy for an Ice object, apart from addressing information, contains the identity of the Ice object. When a client invokes an operation, the object identity is sent with the request to the server. The object adapter receives the request, retrieves the identity, and uses the identity as an index into the servant map. If a servant with that identity is active, the object adapter retrieves the servant from the servant map and dispatches the incoming request into the correct member function on the servant. Assuming that the object adapter is in the , client requests are dispatched to the servant as soon as you call .active state add UUIDs as Identities in Java The Ice object model assumes that object identities are globally unique. One way of ensuring that uniqueness is to use UUIDs (Universally Unique Identifiers) as identities. Java provides a helper function that we can use to create such identities: Java public class Example { public static void main(String[] args) { System.out.println(java.util.UUID.randomUUID().toString()); } } When executed, this program prints a unique string such as . Each call to 5029a22c-e333-4f87-86b1-cd5e0fcce509 randomUUID creates a string that differs from all previous ones. You can use a UUID such as this to create object identities. For convenience, the object adapter has an operation thataddWithUUID generates a UUID and adds a servant to the servant map in a single step. Using this operation, we can create an identity and register a servant with that identity in a single step as follows: Java _adapter.addWithUUID(new NodeI("Fred"));Ice 3.4.2 Documentation 394 Copyright © 2011, ZeroC, Inc. Creating Proxies in Java Once we have activated a servant for an Ice object, the server can process incoming client requests for that object. However, clients can only access the object once they hold a proxy for the object. If a client knows the server's address details and the object identity, it can create a proxy from a string, as we saw in our first example in . However, creation of proxies by the client in thisHello World Application manner is usually only done to allow the client access to initial objects for bootstrapping. Once the client has an initial proxy, it typically obtains further proxies by invoking operations. The object adapter contains all the details that make up the information in a proxy: the addressing and protocol information, and the object identity. The Ice run time offers a number of ways to create proxies. Once created, you can pass a proxy to the client as the return value or as an out-parameter of an operation invocation. Proxies and Servant Activation in Java The and servant activation operations on the object adapter return a proxy for the corresponding Ice object. This meansadd addWithUUID we can write: Java NodePrx proxy = NodePrxHelper.uncheckedCast(_adapter.addWithUUID(new NodeI("Fred"))); Here, both activates the servant and returns a proxy for the Ice object incarnated by that servant in a single step.addWithUUID Note that we need to use an here because returns a proxy of type .uncheckedCast addWithUUID Ice.ObjectPrx Direct Proxy Creation in Java The object adapter offers an operation to create a proxy for a given identity: Slice module Ice { local interface ObjectAdapter { Object* createProxy(Identity id); // ... }; }; Note that creates a proxy for a given identity whether a servant is activated with that identity or not. In other words, proxiescreateProxy have a life cycle that is quite independent from the life cycle of servants: Java Ice.Identity id = new Ice.Identity(); id.name = java.util.UUID.randomUUID().toString(); Ice.ObjectPrx o = _adapter.createProxy(id); This creates a proxy for an Ice object with the identity returned by . Obviously, no servant yet exists for that object so, if werandomUUID return the proxy to a client and the client invokes an operation on the proxy, the client will receive an . (WeObjectNotExistException examine these life cycle issues in more detail in .)Object Life Cycle See Also Hello World Application Server-Side Java Mapping for Interfaces Object Adapter States Servant Locators Object Life CycleIce 3.4.2 Documentation 395 Copyright © 2011, ZeroC, Inc. 1. 2. Asynchronous Method Dispatch (AMD) in Java The number of simultaneous synchronous requests a server is capable of supporting is determined by the number of threads in the server's . If all of the threads are busy dispatching long-running operations, then no threads are available to process new requests andthread pool therefore clients may experience an unacceptable lack of responsiveness. Asynchronous Method Dispatch (AMD), the server-side equivalent of , addresses this scalability issue. Using AMD, a server can receiveAMI a request but then suspend its processing in order to release the dispatch thread as soon as possible. When processing resumes and the results are available, the server sends a response explicitly using a callback object provided by the Ice run time. AMD is transparent to the client, that is, there is no way for a client to distinguish a request that, in the server, is processed synchronously from a request that is processed asynchronously. In practical terms, an AMD operation typically queues the request data (i.e., the callback object and operation arguments) for later processing by an application thread (or thread pool). In this way, the server minimizes the use of dispatch threads and becomes capable of efficiently supporting thousands of simultaneous clients. An alternate use case for AMD is an operation that requires further processing after completing the client's request. In order to minimize the client's delay, the operation returns the results while still in the dispatch thread, and then continues using the dispatch thread for additional work. On this page: Enabling AMD with Metadata in Java AMD Mapping in Java Callback interface for AMD Dispatch method for AMD AMD Exceptions in Java AMD Example in Java Enabling AMD with Metadata in Java To enable asynchronous dispatch, you must add an metadata directive to your Slice definitions. The directive applies at the["amd"] interface and the operation level. If you specify at the interface level, all operations in that interface use asynchronous dispatch; if["amd"] you specify for an individual operation, only that operation uses asynchronous dispatch. In either case, the metadata directive ["amd"] synchronous dispatch, that is, a particular operation implementation must use synchronous or asynchronous dispatch and cannotreplaces use both. Consider the following Slice definitions: Slice ["amd"] interface I { bool isValid(); float computeRate(); }; interface J { ["amd"] void startProcess(); int endProcess(); }; In this example, both operations of interface use asynchronous dispatch, whereas, for interface , uses asynchronousI J startProcess dispatch and uses synchronous dispatch.endProcess Specifying metadata at the operation level (rather than at the interface or class level) minimizes the amount of generated code and, more importantly, minimizes complexity: although the asynchronous model is more flexible, it is also more complicated to use. It is therefore in your best interest to limit the use of the asynchronous model to those operations that need it, while using the simpler synchronous model for the rest. AMD Mapping in Java The Java mapping emits the following code for each AMD operation: Callback interfaceIce 3.4.2 Documentation 396 Copyright © 2011, ZeroC, Inc. 2. Dispatch method Callback interface for AMD A callback interface is used by the implementation to notify the Ice run time about the completion of an operation. The name of this interface is formed using the pattern . For example, an operation named defined in interface results in an interface named AMD_class_op foo I . The interface is generated in the same scope as the interface or class containing the operation. Two methods are provided:AMD_I_foo Java public void ice_response(); The method allows the server to report the successful completion of the operation. If the operation has a non- returnice_response void type, the first parameter to is the return value. Parameters corresponding to the operation's out parameters follow the returnice_response value, in the order of declaration. Java public void ice_exception(java.lang.Exception ex); The method allows the server to raise an exception. With respect to exceptions, there is less compile-time type safety inice_exception an AMD implementation because there is no clause on the dispatch method and any exception type could conceivably be passed tothrows . However, the Ice run time the exception value using the same semantics as for synchronous dispatch.ice_exception validates Neither nor throw any exceptions to the caller.ice_response ice_exception Dispatch method for AMD The dispatch method, whose name has the suffix , has a return type. The first parameter is a reference to an instance of the_async void callback interface described above. The remaining parameters comprise the parameters of the operation, in the order of declaration.in For example, suppose we have defined the following operation: Slice interface I { ["amd"] int foo(short s, out long l); }; The callback interface generated for operation is shown below:foo Java public interface AMD_I_foo { void ice_response(int __ret, long l); void ice_exception(java.lang.Exception ex); } The dispatch method for asynchronous invocation of operation is generated as follows:foo Java void foo_async(AMD_I_foo __cb, short s); AMD Exceptions in Java There are two processing contexts in which the logical implementation of an AMD operation may need to report an exception: the dispatch thread (the thread that receives the invocation), and the response thread (the thread that sends the response).Ice 3.4.2 Documentation 397 Copyright © 2011, ZeroC, Inc. These are not necessarily two different threads: it is legal to send the response from the dispatch thread. Although we recommend that the callback object be used to report all exceptions to the client, it is legal for the implementation to raise an exception instead, but only from the dispatch thread. As you would expect, an exception raised from a response thread cannot be caught by the Ice run time; the application's run time environment determines how such an exception is handled. Therefore, a response thread must ensure that it traps all exceptions and sends the appropriate response using the callback object. Otherwise, if a response thread is terminated by an uncaught exception, the request may never be completed and the client might wait indefinitely for a response. Whether raised in a dispatch thread or reported via the callback object, user exceptions are and local exceptions may undergo validated .translation AMD Example in Java To demonstrate the use of AMD in Ice, let us define the Slice interface for a simple computational engine: Slice module Demo { sequence Row; sequence Grid; exception RangeError {}; interface Model { ["amd"] Grid interpolate(Grid data, float factor) throws RangeError; }; }; Given a two-dimensional grid of floating point values and a factor, the operation returns a new grid of the same size with theinterpolate values interpolated in some interesting (but unspecified) way. Our servant class derives from and supplies a definition for the method that creates a toDemo._ModelDisp interpolate_async Job hold the callback object and arguments, and adds the to a queue. The method is synchronized to guard access to the queue:Job Java public final class ModelI extends Demo._ModelDisp { synchronized public void interpolate_async( Demo.AMD_Model_interpolate cb, float[][] data, float factor, Ice.Current current) throws RangeError { _jobs.add(new Job(cb, data, factor)); } java.util.LinkedList _jobs = new java.util.LinkedList(); } After queuing the information, the operation returns control to the Ice run time, making the dispatch thread available to process another request. An application thread removes the next from the queue and invokes , which uses (not shown) toJob execute interpolateGrid perform the computational work:Ice 3.4.2 Documentation 398 Copyright © 2011, ZeroC, Inc. Java class Job { Job(Demo.AMD_Model_interpolate cb, float[][] grid, float factor) { _cb = cb; _grid = grid; _factor = factor; } void execute() { if (!interpolateGrid()) { _cb.ice_exception(new Demo.RangeError()); return; } _cb.ice_response(_grid); } private boolean interpolateGrid() { // ... } private Demo.AMD_Model_interpolate _cb; private float[][] _grid; private float _factor; } If returns , then is invoked to indicate that a range error has occurred. The statementinterpolateGrid false ice_exception return following the call to is necessary because does not throw an exception; it only marshals the exceptionice_exception ice_exception argument and sends it to the client. If interpolation was successful, is called to send the modified grid back to the client.ice_response See Also User Exceptions Run-Time Exceptions Asynchronous Method Invocation (AMI) in Java The Ice Threading ModelIce 3.4.2 Documentation 399 Copyright © 2011, ZeroC, Inc. Example of a File System Server in Java This page presents the source code for a Java server that implements our and communicates with the we wrote earlier.file system client The code is fully functional, apart from the required interlocking for threads. Note that the server is remarkably free of code that relates to distribution: most of the server code is simply application logic that would be present just the same for a non-distributed version. Again, this is one of the major advantages of Ice: distribution concerns are kept away from application code so that you can concentrate on developing application logic instead of networking infrastructure. The server code shown here is not quite correct as it stands: if two clients access the same file in parallel, each via a different thread, one thread may read the data member while another thread updates it. Obviously, if that_lines happens, we may write or return garbage or, worse, crash the server. However, it is trivial to make the and read write operations thread-safe. We discuss thread safety in .The Ice Threading Model On this page: Implementing a File System Server in Java Server Main Program in Java Servant Class in JavaFileI Servant Class in JavaDirectoryI Data MembersDirectoryI ConstructorDirectoryI MethodsDirectoryI Implementing a File System Server in Java We have now seen enough of the server-side Java mapping to implement a server for our . (You may find it useful to review thesefile system Slice definitions before studying the source code.) Our server is composed of three source files: Server.java This file contains the server main program. Filesystem/DirectoryI.java This file contains the implementation for the servants.Directory Filesystem/FileI.java This file contains the implementation for the servants.File Server Main Program in Java Our server main program, in the file , uses the class. The method installs a shutdown hook, createsServer.java Ice.Application run an object adapter, instantiates a few servants for the directories and files in the file system, and then activates the adapter. This leads to a main program as follows: Java import Filesystem.*; public class Server extends Ice.Application { public int run(String[] args) { // // Terminate cleanly on receipt of a signal // shutdownOnInterrupt(); // Create an object adapter (stored in the _adapter // static members) //Ice 3.4.2 Documentation 400 Copyright © 2011, ZeroC, Inc. Ice.ObjectAdapter adapter = communicator().createObjectAdapterWithEndpoints( "SimpleFilesystem", "default -p 10000"); DirectoryI._adapter = adapter; FileI._adapter = adapter; // Create the root directory (with name "/" and no parent) // DirectoryI root = new DirectoryI("/", null); // Create a file "README" in the root directory // File file = new FileI("README", root); String[] text; text = new String[] { "This file system contains a collection of poetry." }; try { file.write(text, null); } catch (GenericError e) { System.err.println(e.reason); } // Create a directory "Coleridge" in the root directory // DirectoryI coleridge = new DirectoryI("Coleridge", root); // Create a file "Kubla_Khan" in the Coleridge directory // file = new FileI("Kubla_Khan", coleridge); text = new String[]{ "In Xanadu did Kubla Khan", "A stately pleasure-dome decree:", "Where Alph, the sacred river, ran", "Through caverns measureless to man", "Down to a sunless sea." }; try { file.write(text, null); } catch (GenericError e) { System.err.println(e.reason); } // All objects are created, allow client requests now // adapter.activate(); // Wait until we are done // communicator().waitForShutdown(); return 0; } public static void main(String[] args) { Server app = new Server(); System.exit(app.main("Server", args));Ice 3.4.2 Documentation 401 Copyright © 2011, ZeroC, Inc. } } The code imports the contents of the package. This avoids having to continuously use fully-qualified identifiers with a Filesystem prefix.Filesystem. The next part of the source code is the definition of the class, which derives from and contains the mainServer Ice.Application application logic in its method. Much of this code is boiler plate that we saw previously: we create an object adapter, and, towards therun end, activate the object adapter and call .waitForShutdown The interesting part of the code follows the adapter creation: here, the server instantiates a few nodes for our file system to create the structure shown below: A small file system. As we will see shortly, the servants for our directories and files are of type and , respectively. The constructor for eitherDirectoryI FileI type of servant accepts two parameters, the name of the directory or file to be created and a reference to the servant for the parent directory. (For the root directory, which has no parent, we pass a null parent.) Thus, the statement Java DirectoryI root = new DirectoryI("/", null); creates the root directory, with the name and no parent directory."/" Here is the code that establishes the structure in the above illustration:Ice 3.4.2 Documentation 402 Copyright © 2011, ZeroC, Inc. Java // Create the root directory (with name "/" and no parent) // DirectoryI root = new DirectoryI("/", null); // Create a file "README" in the root directory // File file = new FileI("README", root); String[] text; text = new String[] { "This file system contains a collection of poetry." }; try { file.write(text, null); } catch (GenericError e) { System.err.println(e.reason); } // Create a directory "Coleridge" in the root directory // DirectoryI coleridge = new DirectoryI("Coleridge", root); // Create a file "Kubla_Khan" in the Coleridge directory // file = new FileI("Kubla_Khan", coleridge); text = new String[]{ "In Xanadu did Kubla Khan", "A stately pleasure-dome decree:", "Where Alph, the sacred river, ran", "Through caverns measureless to man", "Down to a sunless sea." }; try { file.write(text, null); } catch (GenericError e) { System.err.println(e.reason); } We first create the root directory and a file within the root directory. (Note that we pass a reference to the root directory as theREADME parent when we create the new node of type .)FileI The next step is to fill the file with text: Java String[] text; text = new String[] { "This file system contains a collection of poetry." }; try { file.write(text, null); } catch (GenericError e) { System.err.println(e.reason); } Recall that by default map to Java arrays. The Slice type is simply an array of strings; we add a line of text to our Slice sequences Lines file by initializing the array to contain one element.README text Finally, we call the Slice operation on our servant by writing:write FileI Java file.write(text, null);Ice 3.4.2 Documentation 403 Copyright © 2011, ZeroC, Inc. This statement is interesting: the server code invokes an operation on one of its own servants. Because the call happens via a reference to the servant (of type ) and via a proxy (of type ), the Ice run time does not know that this call is even taking place — suchFileI not FilePrx a direct call into a servant is not mediated by the Ice run time in any way and is dispatched as an ordinary Java function call. In similar fashion, the remainder of the code creates a subdirectory called and, within that directory, a file called toColeridge Kubla_Khan complete the structure in the illustration listed above. FileI Servant Class in Java Our servant class has the following basic structure:FileI Java public class FileI extends _FileDisp { // Constructor and operations here... public static Ice.ObjectAdapter _adapter; private String _name; private DirectoryI _parent; private String[] _lines; } The class has a number of data members: _adapter This static member stores a reference to the single object adapter we use in our server. _name This member stores the name of the file incarnated by the servant. _parent This member stores the reference to the servant for the file's parent directory. _lines This member holds the contents of the file. The and data members are initialized by the constructor:_name _parent Java public FileI(String name, DirectoryI parent) { _name = name; _parent = parent; assert(_parent != null); // Create an identity // Ice.Identity myID = new Ice.Identity(); myID.name = java.util.UUID.randomUUID().toString(); // Add the identity to the object adapter // _adapter.add(this, myID); // Create a proxy for the new node and // add it as a child to the parent // NodePrx thisNode = NodePrxHelper.uncheckedCast(_adapter.createProxy(myID)); _parent.addChild(thisNode); }Ice 3.4.2 Documentation 404 Copyright © 2011, ZeroC, Inc. After initializing the and members, the code verifies that the reference to the parent is not null because every file must have_name _parent a parent directory. The constructor then generates an identity for the file by calling and adds itself to thejava.util.UUID.randomUUID servant map by calling . Finally, the constructor creates a proxy for this file and calls the method on itsObjectAdapter.add addChild parent directory. is a helper function that a child directory or file calls to add itself to the list of descendant nodes of its parentaddChild directory. We will see the implementation of this function in . MethodsDirectoryI The remaining methods of the class implement the Slice operations we defined in the and Slice interfaces:FileI Node File Java // Slice Node::name() operation public String name(Ice.Current current) { return _name; } // Slice File::read() operation public String[] read(Ice.Current current) { return _lines; } // Slice File::write() operation public void write(String[] text, Ice.Current current) throws GenericError { _lines = text; } The method is inherited from the generated interface (which is a base interface of the class from which isname Node _FileDisp FileI derived). It returns the value of the member._name The and methods are inherited from the generated interface (which is a base interface of the class fromread write File _FileDisp which is derived) and return and set the member.FileI _lines DirectoryI Servant Class in Java The class has the following basic structure:DirectoryI Java package Filesystem; public final class DirectoryI extends _DirectoryDisp { // Constructor and operations here... public static Ice.ObjectAdapter _adapter; private String _name; private DirectoryI _parent; private java.util.ArrayList _contents = new java.util.ArrayList(); } DirectoryI Data Members As for the class, we have data members to store the object adapter, the name, and the parent directory. (For the root directory, the FileI member holds a null reference.) In addition, we have a data member that stores the list of child directories. These_parent _contents data members are initialized by the constructor:Ice 3.4.2 Documentation 405 Copyright © 2011, ZeroC, Inc. Java public DirectoryI(String name, DirectoryI parent) { _name = name; _parent = parent; // Create an identity. The parent has the // fixed identity "RootDir" // Ice.Identity myID = new Ice.Identity(); myID.name = _parent != null ? java.util.UUID.randomUUID().toString() : "RootDir"; // Add the identity to the object adapter // _adapter.add(this, myID); // Create a proxy for the new node and add it as a // child to the parent // NodePrx thisNode = NodePrxHelper.uncheckedCast(_adapter.createProxy(myID)); if (_parent != null) _parent.addChild(thisNode); } DirectoryI Constructor The constructor creates an identity for the new directory by calling . (For the root directory, we use thejava.util.UUID.randomUUID fixed identity .) The servant adds itself to the servant map by calling and then creates a reference to itself"RootDir" ObjectAdapter.add and passes it to the helper function.addChild DirectoryI Methods addChild adds the passed reference to the list:_contents Java void addChild(NodePrx child) { _contents.add(child); } The remainder of the operations, and , are equally trivial:name listIce 3.4.2 Documentation 406 Copyright © 2011, ZeroC, Inc. Java public String name(Ice.Current current) { return _name; } // Slice Directory::list() operation public NodePrx[] list(Ice.Current current) { NodePrx[] result = new NodePrx[_contents.size()]; _contents.toArray(result); return result; } Note that the member is of type , which is convenient for the implementation of the _contents java.util.ArrayList method. However, this requires us to convert the list into a Java array in order to return it from the operation.addChild list See Also Slice for a Simple File System Example of a File System Client in Java The Server-Side main Method in Java Java Mapping for Sequences The Ice Threading ModelIce 3.4.2 Documentation 407 Copyright © 2011, ZeroC, Inc. The Java Utility Library Ice for Java includes a number of utility APIs in the package and the class. This section summarizes the contents ofIceUtil Ice.Util these APIs for your reference. On this page: The Package in JavaIceUtil and ClassesCache Store The Class in JavaIce.Util Communicator Initialization Methods Identity Conversion Per-Process Logger Methods Property Creation Methods Proxy Comparison Methods Stream Creation Version Information The Package in JavaIceUtil Cache and ClassesStore The class allows you to efficiently maintain a cache that is backed by secondary storage, such as a Berkeley DB database, withoutCache holding a lock on the entire cache while values are being loaded from the database. If you want to create that store theirevictors for servants state in a database, the class can simplify your evictor implementation considerably.Cache You may also want to examine the implementation of the Freeze background save evictor in the source distribution; it uses for its implementation.IceUtil.Cache The class has the following interface:Cache Java package IceUtil; public class Cache { public Cache(Store store); public Object pin(Object key); public Object pin(Object key, Object o); public Object unpin(Object key); public Object putIfAbsent(Object key, Object newObj); public Object getIfPinned(Object key); public void clear(); public int size(); } Internally, a maintains a map of name-value pairs. The implementation of takes care of maintaining the map; in particular, itCache Cache ensures that concurrent lookups by callers are possible without blocking even if some of the callers are currently loading values from the backing store. In turn, this is useful for evictor implementations, such as the Freeze . The class does not limitbackground save evictor Cache the number of entries in the cache — it is the job of the evictor implementation to limit the map size by calling on elements of the mapunpin that it wants to evict. The class works in conjunction with a interface for which you must provide an implementation. The interface is trivial:Cache Store StoreIce 3.4.2 Documentation 408 Copyright © 2011, ZeroC, Inc. Java package IceUtil; public interface Store { Object load(Object key); } You must implement the method in a class that you derive from . The implementation calls when it needs toload Store Cache load retrieve the value for the passed key from the backing store. If cannot locate a record for the given key because no such record exists,load it must return null. If fails for some other reason, it can throw an exception derived from , which isload java.lang.RuntimeException propagated back to the application code. The public member functions of behave as follows:Cache Cache(Store s) The constructor initializes the cache with your implementation of the interface.Store Object pin(Object key, Object val) To add a key-value pair to the cache, your evictor can call . The return value is null if the key and value were added; otherwise, if thepin map already contains an entry with the given key, the entry is unchanged and returns the original value for that key.pin This version of does call to retrieve the entry from backing store if it is not yet in the cache. This is useful when you add apin not load newly-created object to the cache. Object pin(Object key) This version of returns the value stored in the cache for the given key if the cache already contains an entry for that key. If no entry withpin the given key is in the cache, calls to retrieve the corresponding value (if any) from the backing store. returns the valuepin load pin returned by , that is, the value if could retrieve it, null if could not retrieve it, or any exception thrown by .load load load load Object unpin(Object key) unpin removes the entry for the given key from the cache. If the cache contained an entry for the key, the return value is the value for that key; otherwise, the return value is null. Object putIfAbsent(Object key, Object val) This function adds a key-value pair to the cache. If the cache already contains an entry for the given key, returns the originalputIfAbsent value for that key. If no entry with the given key is in the cache, calls to retrieve the corresponding entry (if any) fromputIfAbsent load the backing store and returns the value returned by .load If the cache does not contain an entry for the given key and does not retrieve a value for the key, the method adds the new entry andload returns null. Object getIfPinned(Object key) This function returns the value stored for the given key. If an entry for the given key is in the map, the function returns the corresponding value; otherwise, the function returns null. does not call .getIfPinned load void clear() This function removes all entries in the map. int size() This function returns the number of entries in the map. The Class in JavaIce.UtilIce 3.4.2 Documentation 409 Copyright © 2011, ZeroC, Inc. Communicator Initialization Methods Ice.Util provides a number of overloaded methods that .initialize create a communicator Identity Conversion Ice.Util contains two methods for of type to and from strings.converting object identities Ice.Identity Per-Process Logger Methods Ice.Util provides methods for getting and setting the .per-process logger Property Creation Methods Ice.Util provides a number of overloaded methods that .createProperties create property sets Proxy Comparison Methods Two methods, and , allow you to that are storedproxyIdentityCompare proxyIdentityAndFacetCompare compare object identities in proxies (either ignoring the facet or taking the facet into account). Stream Creation Two methods, and create for use with dynamic invocation.createInputStream createOutputStream streams Version Information The and methods return the version of the Ice run time:stringVersion intVersion Java public static String stringVersion(); public static int intVersion(); The method returns the Ice version in the form , for example, . For beta releases, thestringVersion .. 3.4.2 version is , for example, .. b 3.4b The method returns the Ice version in the form , where is the major version number, is the minor versionintVersion AABBCC AA BB number, and is patch level, for example, for version 3.4.2. For beta releases, the patch level is set to 51 so, for example, forCC 30402 version 3.4b, the value is .30451 See Also Background Save Evictor Java Mapping for Interfaces Command-Line Parsing and Initialization Setting Properties Object Identity Java Streaming InterfacesIce 3.4.2 Documentation 410 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping Topics Client-Side Slice-to-C-Sharp Mapping Server-Side Slice-to-C-Sharp Mapping .NET Compact Framework Support The .NET Utility LibraryIce 3.4.2 Documentation 411 Copyright © 2011, ZeroC, Inc. Client-Side Slice-to-C-Sharp Mapping The client-side Slice-to-C# mapping defines how Slice data types are translated to C# types, and how clients invoke operations, pass parameters, and handle errors. Much of the C# mapping is intuitive. For example, by default, Slice sequences map to C# arrays, so there is little you have learn in order to use Slice sequences in C#. The C# API to the Ice run time is fully thread-safe. Obviously, you must still synchronize access to data from different threads. For example, if you have two threads sharing a sequence, you cannot safely have one thread insert into the sequence while another thread is iterating over the sequence. However, you only need to concern yourself with concurrent access to your own data — the Ice run time itself is fully thread safe, and none of the Ice API calls require you to acquire or release a lock before you safely can make the call. Much of what appears in this chapter is reference material. We suggest that you skim the material on the initial reading and refer back to specific sections as needed. However, we recommend that you read at least the mappings for , , and inexceptions interfaces operations detail because these sections cover how to call operations from a client, pass parameters, and handle exceptions. In order to use the C# mapping, you should need no more than the Slice definition of your application and knowledge of the C# mapping rules. In particular, looking through the generated code in order to discern how to use the C# mapping is likely to be inefficient, due to the amount of detail. Of course, occasionally, you may want to refer to the generated code to confirm a detail of the mapping, but we recommend that you otherwise use the material presented here to see how to write your client-side code. The NamespaceIce All of the APIs for the Ice run time are nested in the namespace, to avoid clashes with definitions for other libraries orIce applications. Some of the contents of the namespace are generated from Slice definitions; other parts of the Ice Ice namespace provide special-purpose definitions that do not have a corresponding Slice definition. We will incrementally cover the contents of the namespace throughout the remainder of the manual.Ice Topics C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for Exceptions C-Sharp Mapping for Interfaces C-Sharp Mapping for Operations C-Sharp Mapping for Classes Serializable Objects in C-Sharp C-Sharp Specific Metadata Directives Asynchronous Method Invocation (AMI) in C-Sharp slice2cs Command-Line Options Using Slice Checksums in C-Sharp Example of a File System Client in C-SharpIce 3.4.2 Documentation 412 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Identifiers Slice identifiers map to an identical C# identifier. For example, the Slice identifier becomes the C# identifier . If a Slice identifierClock Clock is the same as a C# keyword, the corresponding C# identifier is a (an identifier prefixed with @). For example, the Sliceverbatim identifier identifier is mapped as .while @while You should try to as much as possible.avoid such identifiers The Slice-to-C# compiler generates classes that inherit from interfaces or base classes in the .NET framework. These interfaces and classes introduce a number of methods into derived classes. To avoid name clashes between Slice identifiers that happen to be the same as an inherited method, such identifiers are prefixed with and suffixed with in the generated code. For example, the Slice identifier ice_ _ Clone maps to the C# identifier if it would clash with an inherited . The complete list of identifiers that are so changed is:ice_Clone_ Clone Clone Equals Finalize GetBaseException GetHashCode GetObjectData GetType MemberwiseClone ReferenceEquals ToString checkedCast uncheckedCast Note that Slice identifiers in this list are translated to the corresponding C# identifier only where necessary. For example, structures do not derive from , so if a Slice structure contains a member named , the corresponding C# structure's member is named ICloneable Clone as well. On the other hand, classes do derive from , so, if a Slice class contains a member named , theClone ICloneable Clone corresponding C# class's member is named .ice_Clone_ Also note that, for the purpose of prefixing, Slice identifiers are case-insensitive, that is, both and are escaped and map to Clone clone and , respectively.ice_Clone_ ice_clone_ See Also Identifiers That Are Keywords C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 413 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Modules Slice modules map to C# namespaces with the same name as the Slice module. The mapping preserves the nesting of the Slice definitions. For example: Slice module M1 { // Definitions for M1 here... module M2 { // Definitions for M2 here... }; }; // ... module M1 { // Reopen M1 // More definitions for M1 here... }; This definition maps to the corresponding C# definitions: C# namespace M1 { namespace M2 { // ... } // ... } // ... namespace M1 // Reopen M1 { // ... } If a Slice module is reopened, the corresponding C# namespace is reopened as well. See Also C-Sharp Mapping for Identifiers C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 414 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Built-In Types The Slice built-in types are mapped to C# types as shown below: Slice C# bool bool byte byte short short int int long long float float double double string string Mapping of Slice built-in types to C#. See Also C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 415 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Enumerations A Slice maps to the corresponding enumeration in C#. For example:enumeration Slice enum Fruit { Apple, Pear, Orange }; Not surprisingly, the generated C# definition is identical: C# enum Fruit { Apple, Pear, Orange }; See Also C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 416 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Structures Ice for .NET supports two different mappings for Slice . By default, Slice structures map to C# structures if they (recursively)structures contain only value types. If a Slice structure (recursively) contains a string, proxy, class, sequence, or dictionary member, it maps to a C# class. A allows you to force the mapping to a C# class for Slice structures that contain only value types.metadata directive In addition, for either mapping, you can control whether Slice data members are mapped to fields or to .properties On this page: Structure Mapping for Structures in C# Class Mapping for Structures in C# Property Mapping for Structures in C# Structure Mapping for Structures in C# Consider the following structure: Slice struct Point { double x; double y; }; This structure consists of only value types and so, by default, maps to a C# partial structure: C# public partial struct Point { public double x; public double y; public Point(double x, double y); public override int GetHashCode(); public override bool Equals(object other); public static bool operator==(Point lhs, Point rhs); public static bool operator!=(Point lhs, Point rhs); } For each data member in the Slice definition, the C# structure contains a corresponding public data member of the same name. The generated constructor accepts one argument for each structure member, in the order in which they are defined in the Slice definition. This allows you to construct and initialize a structure in a single statement: C# Point p = new Point(5.1, 7.8); Note that C# does not allow a value type to declare a default constructor or to assign default values to data members. The structure overrides the and methods to allow you to use it as the key type of a dictionary. (Note that the staticGetHashCode Equals two-argument version of is inherited from .) Two structures are equal if (recursively) all their data members areEquals System.Object equal. Otherwise, they are not equal. For structures that contain reference types, performs a deep comparison; that is, referenceEquals types are compared for value equality, not reference equality. Class Mapping for Structures in C#Ice 3.4.2 Documentation 417 Copyright © 2011, ZeroC, Inc. The mapping for Slice structures to C# structures provides value semantics. Usually, this is appropriate, but there are situations where you may want to change this: If you use structures as members of a collection, each access to an element of the collection incurs the cost of boxing or unboxing. Depending on your situation, the performance penalty may be noticeable. On occasion, it is useful to be able to assign null to a structure, for example, to support "not there" semantics (such as when implementing parameters that are conceptually optional). To allow you to choose the correct performance and functionality trade-off, the Slice-to-C# compiler provides an alternative mapping of structures to classes, for example: Slice ["clr:class"] struct Point { double x; double y; }; The metadata directive instructs the Slice-to-C# compiler to generate a mapping to a C# partial class for this structure. The"clr:class" generated code is almost identical, except that the keyword is replaced by the keyword and that the class has a defaultstruct class constructor and inherits from :ICloneable C# public partial class Point : _System.ICloneable { public double x; public double y; public Point(); public Point(double x, double y); public object Clone(); public override int GetHashCode(); public override bool Equals(object other); public static bool operator==(Point lhs, Point rhs); public static bool operator!=(Point lhs, Point rhs); } Some of the generated marshaling code differs for the class mapping of structures, but this is irrelevant to application code. The class has a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize a member whose type is a class-mapped structure because the Ice run time does not accept null as a legal value for these types. If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition The class also provides a second constructor that has one parameter for each data member. This allows you to construct and initialize a class instance in a single statement: C# Point p = new Point(5.1, 7.8); The method performs a shallow memberwise copy, and the comparison methods have the usual semantics (they perform valueClone comparison). Note that you can influence the mapping for structures only at the point of definition of a structure, that is, for a particular structure type, you must decide whether you want to use the structure or the class mapping. (You cannot override the structure mapping elsewhere, forIce 3.4.2 Documentation 418 Copyright © 2011, ZeroC, Inc. example, for individual structure members or operation parameters.) As we mentioned previously, if a Slice structure (recursively) contains a member of reference type, it is automatically mapped to a C# class. (The compiler behaves as if you had explicitly specified the metadata directive for the structure.)"clr:class" Here is our structure once more:Employee Slice struct Employee { long number; string firstName; string lastName; }; The structure contains two strings, which are reference types, so the Slice-to-C# compiler generates a C# class for this structure: C# public partial class Employee : _System.ICloneable { public long number; public string firstName; public string lastName; public Employee(); public Employee(long number, string firstName, string lastName); public object Clone(); public override int GetHashCode(); public override bool Equals(object other); public static bool operator==(Employee lhs, Employee rhs); public static bool operator!=(Employee lhs, Employee rhs); } Property Mapping for Structures in C# You can instruct the compiler to emit property definitions instead of public data members. For example: Slice ["clr:property"] struct Point { double x; double y; }; The metadata directive causes the compiler to generate a property for each Slice data member:"clr:property"Ice 3.4.2 Documentation 419 Copyright © 2011, ZeroC, Inc. C# public partial struct Point { private double x_prop; public double x { get { return x_prop; } set { x_prop = value; } } private double y_prop; public double y { get { return y_prop; } set { y_prop = value; } } // Other methods here... } Note that the properties are non-virtual because C# structures cannot have virtual properties. However, if you apply the "clr:property" directive to a structure that contains a member of reference type, or if you combine the and directives, the"clr:property" "clr:class" generated properties are virtual. For example: Slice ["clr:property", "clr:class"] struct Point { double x; double y; }; This generates the following code:Ice 3.4.2 Documentation 420 Copyright © 2011, ZeroC, Inc. C# public partial class Point : System.ICloneable { private double x_prop; public virtual double x { get { return x_prop; } set { x_prop = value; } } private double y_prop; public virtual double y { get { return y_prop; } set { y_prop = value; } } // Other methods here... } See Also Metadata C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 421 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Sequences Ice for .NET supports several different mappings for . By default, sequences are mapped to arrays. You can use sequences metadata to map sequences to a number of alternative types:directives System.Collections.Generic.List System.Collections.Generic.LinkedList System.Collections.Generic.Queue System.Collections.Generic.Stack Types derived from , which is a drop-in replacement for (thisIce.CollectionBase System.Collections.CollectionBase mapping is provided mainly for compatibility with Ice versions prior to 3.3) User-defined custom types that derive from .System.Collections.Generic.IEnumerable The different mappings allow you to map sequences to a container type that provides the correct performance trade-off for your application. On this page: Array Mapping for Sequences in C# Mapping to Predefined Generic Containers for Sequences in C# Mapping to Custom Types for Sequences in C# Mapping for Sequences in C#CollectionBase Multi-Dimensional Sequences in C# Array Mapping for Sequences in C# By default, the Slice-to-C# compiler maps sequences to arrays. Interestingly, no code is generated in this case; you simply define an array of elements to model the Slice sequence. For example: Slice sequence FruitPlatter; Given this definition, to create a sequence containing an apple and an orange, you could write: C# Fruit[] fp = { Fruit.Apple, Fruit.Orange }; Or, alternatively: C# Fruit fp[] = new Fruit[2]; fp[0] = Fruit.Apple; fp[1] = Fruit.Orange; The array mapping for sequences is both simple and efficient, especially for sequences that do not need to provide insertion or deletion other than at the end of the sequence. Mapping to Predefined Generic Containers for Sequences in C# With metadata directives, you can change the default mapping for sequences to use generic containers provided by .NET. For example:Ice 3.4.2 Documentation 422 Copyright © 2011, ZeroC, Inc. Slice ["clr:generic:List"] sequence StringSeq; ["clr:generic:LinkedList"] sequence FruitSeq; ["clr:generic:Queue"] sequence IntQueue; ["clr:generic:Stack"] sequence DoubleStack; The < > metadata directive causes the compiler to the map the corresponding sequence to one of the"clr:generic: type " slice2cs containers in the namespace. For example, the sequence maps to System.Collections.Generic Queue due to its metadata directive.System.Collections.Generic.Queue The predefined containers allow you to select an appropriate space-performance trade-off, depending on how your application uses a sequence. In addition, if a sequence contains value types, such as , the generic containers do not incur the cost of boxing and unboxingint and so are quite efficient. (For example, performs within a few percentage points of anSystem.Collections.Generic.List integer array for insertion and deletion at the end of the sequence, but has the advantage of providing a richer set of operations.) Generic containers can be used for sequences of any element type except objects. For sequences of objects, only is supportedList because it provides the functionality required for efficient unmarshaling. Metadata that specifies any other generic type is ignored with a warning: Slice class MyClass { // ... }; ["clr:generic:List"] sequence MyClassList; // OK ["clr:generic:LinkedList"] sequence MyClassLinkedList; // Ignored In this example, sequence type maps to the generic container , butMyClassList System.Collections.Generic.List sequence type uses the default array mapping.MyClassLinkedList Mapping to Custom Types for Sequences in C# If the array mapping and the predefined containers are unsuitable for your application (for example, because you may need a priority queue, which does not come with .NET), you can implement your own custom containers and direct to map sequences to these customslice2cs containers. For example: Slice ["clr:generic:MyTypes.PriorityQueue"] sequence Queue; This metadata directive causes the Slice sequence to be mapped to the type . You must specify theQueue MyTypes.PriorityQueue fully-qualified name of your custom type following the prefix. This is because the generated code prepends a clr:generic: global:: qualifier to the type name you provide; for the preceding example, the generated code refers to your custom type as .global::MyTypes.PriorityQueue Your custom type can have whatever interface you deem appropriate, but it must meet the following requirements: The custom type must derive from .System.Collections.Generic.IEnumerable The custom type must provide a readable property that returns the number of elements in the collection.Count The custom type must provide an method that appends an element to the end of the collection.Add If (and only if) the Slice sequence contains elements that are Slice classes, the custom type must provide an indexer that sets the value of an element at a specific index. (Indexes, as usual, start at zero.) As an example, here is a minimal class (omitting implementation) that meets these criteria:Ice 3.4.2 Documentation 423 Copyright © 2011, ZeroC, Inc. C# public class PriorityQueue : IEnumerable { public IEnumerator GetEnumerator(); public int Count get; public void Add(T elmt); public T this[int index] // Needed for class elements only. set; // Other methods and data members here... } CollectionBase Mapping for Sequences in C# The mapping is provided mainly for compatibility with Ice versions prior to 3.3. Internally, isCollectionBase CollectionBase implemented using , so it offers the same performance trade-offs as . (For valueSystem.Collections.Generic.List List types, is considerably faster than , however.)Ice.CollectionBase System.Collections.CollectionBase Ice.CollectionBase is not as type-safe as because, in order to remain source-code compatible with List , it provides methods that accept elements of type . This means that, if you pass anSystem.Collections.CollectionBase object element of the wrong type, the problem will be diagnosed only at run time, instead of at compile time. For this reason, we suggest that you do not use the mapping for new code.CollectionBase To enable the mapping, you must use the metadata directive:CollectionBase "clr:collection" Slice ["clr:collection"] sequence FruitPlatter; With this directive, generates a type that derives from :slice2cs Ice.CollectionBase C# public class FruitPlatter : Ice.CollectionBase, System.ICloneable { public FruitPlatter(); public FruitPlatter(int capacity); public FruitPlatter(Fruit[] a); public FruitPlatter(System.Collections.Generic.IEnumerable l); public static implicit operator _System.Collections.Generic.List(FruitPlatter s); public virtual FruitPlatter GetRange(int index, int count); public static FruitPlatter Repeat(Fruit value, int count); public object Clone(); } The generated class provides the following methods:FruitPlatter FruitPlatter(); FruitPlatter(int capacity); FruitPlatter(Fruit[] a); FruitPlatter(IEnumerable l); Apart from calling the default constructor, you can also specify an initial capacity for the sequence or, using the array constructor,Ice 3.4.2 Documentation 424 Copyright © 2011, ZeroC, Inc. initialize a sequence from an array. In addition, you can initialize the class to contain the same elements as any enumerable collection with the same element type. FruitPlatter GetRange(int index, int count); This method returns a new sequence with elements that are copied from the source sequence beginning at .count index FruitPlatter Repeat(Fruit value, int count); This method returns a sequence with elements that are initialized to .count value object Clone() The method returns a shallow copy of the source sequence.Clone static implicit operator List (FruitPlatter s); This operator performs an implicit conversion of a instance to a , so you can pass a FruitPlatter List FruitPlatter sequence where a , , or is expected.List IEnumerable System.Collections.IEnumerable The remaining methods are provided by the generic base class. This class provides the following methods:Ice.CollectionBase CollectionBase(); CollectionBase(int capacity); CollectionBase(T[] a); CollectionBase(IEnumerable l); The constructors initialize the sequence as for the concrete derived class. int Count { get; } This property returns the number of elements of the sequence. int Capacity { get; set; } This property controls the capacity of the sequence. Its semantics are as for the corresponding property of .List virtual void TrimToSize(); This method sets the capacity of the sequence to the actual number of elements. int Add(object o); int Add(T value); These methods append at the end of the sequence. They return the index at which the element is inserted (which always isvalue the value of prior the call to .)Count Add void Insert(int index, object o); void Insert(int index, T value); These methods insert an element at the specified index. virtual void InsertRange(int index, CollectionBase c); virtual void InsertRange(int index, T[] c); These methods insert a range of values into the sequence starting at the given index. virtual void SetRange(int index, CollectionBase c); virtual void SetRange(int index, T[] c); These methods copy the provided sequence over a range of elements in the target sequence, starting at the provided index, with semantics as for .System.Collections.ArrayList void RemoveAt(int index); This method deletes the element at the specified index. void Remove(object o); void Remove(T value); These methods search for the specified element and, if present, delete that element. If the element is not in the sequence, the methods do nothing. virtual void RemoveRange(int index, int count); This method removes elements, starting at the given index.count void Clear(); This method deletes all elements of the sequence. bool Contains(object o); bool Contains(T value); These methods return true if the sequence contains ; otherwise, they return false.value int IndexOf(object o);Ice 3.4.2 Documentation 425 Copyright © 2011, ZeroC, Inc. int IndexOf(T value); These methods return the index of the specified element. If the element is not in the sequence, the return value is .-1 virtual int LastIndexOf(T value); virtual int LastIndexOf(T value, int startIndex); virtual int LastIndexOf(T value, int startIndex, int count); These methods search for the provided element and return its last occurrence in the sequence, as for .System.Collections.ArrayList.LastIndexOf object this[int index] { get; set; } T this[int index] { get; set; } The indexers allow you to read and write elements using array subscript notation. IEnumerator GetEnumerator(); This method returns an enumerator that you can use to iterate over the collection. static implicit operator List (CollectionBase s); As for the derived class, this operator permits implicit conversion to a .List void CopyTo(T[] a); void CopyTo(T[] a, int i); void CopyTo(int i, T[] a, int ai, int c); void CopyTo(System.Array a, int i); These methods copy the contents of a sequence into an array. The semantics are the same as for the corresponding methods of .List T[] ToArray(); The method returns the contents of the sequence as an array.ToArray void AddRange(CollectionBase s); void AddRange(T[] a); The methods append the contents of a sequence or an array to the current sequence, respectively.AddRange virtual void Sort(); virtual void Sort(System.Collections.IComparer comparer); virtual void Sort(int index, int count, System.Collections.IComparer comparer); These methods sort the sequence. virtual void Reverse(); virtual void Reverse(int index, int count); These methods reverse the order of elements of the sequence. virtual int BinarySearch(T value); virtual int BinarySearch(T value, System.Collections.IComparer comparer); virtual int BinarySearch(int index, int count, T value, System.Collections.IComparer comparer); The methods perform a binary search on the sequence, with semantics as for .System.Collections.ArrayList static FruitPlatter Repeat(Fruit value, int count); This method returns a sequence with elements that are initialized to .count value Note that for all methods that return sequences, these methods perform a shallow copy, that is, if you have a sequence whose elements have reference type, what is copied are the references, not the objects denoted by those references. Ice.CollectionBase also provides the usual and methods, as well as the comparison operators for equality andGetHashCode Equals inequality. (Two sequences are equal if they have the same number of elements and all elements in corresponding positions are equal, as determined by the method of the elements.)Equals Ice.CollectionBase also implements the inherited , , and properties (which return false),IsFixedSize IsReadOnly IsSynchronized and the inherited property (which returns ).SyncRoot this Creating a sequence containing an apple and an orange is simply a matter of writing: C# FruitPlatter fp = new FruitPlatter(); fp.Add(Fruit.Apple); fp.Add(Fruit.Orange);Ice 3.4.2 Documentation 426 Copyright © 2011, ZeroC, Inc. Multi-Dimensional Sequences in C# Slice permits you to define sequences of sequences, for example: Slice enum Fruit { Apple, Orange, Pear }; ["clr:generic:List"] sequence FruitPlatter; ["clr:generic:LinkedList"] sequence Cornucopia; If we use these definitions as shown, the type of FruitPlatter in the generated code is: C# System.Collections.Generic.LinkedList> Here the outer sequence contains elements of type , as you would expect.List Now let us modify the definition to change the mapping of to an array:FruitPlatter Slice enum Fruit { Apple, Orange, Pear }; sequence FruitPlatter; ["clr:LinkedList"] sequence Cornucopia; With this definition, the type of becomes:Cornucopia C# System.Collections.Generic.LinkedList The generated code now no longer mentions the type anywhere and deals with the outer sequence elements as an array of FruitPlatter instead.Fruit See Also Metadata C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 427 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Dictionaries Ice for .NET supports three different mappings for dictionaries. By default, dictionaries are mapped to . You can use to map dictionaries to two other types:System.Collections.Generic.Dictionary metadata directives System.Collections.Generic.SortedDictionary Types derived from , which is a drop-in replacement for (thisIce.DictionaryBase System.Collections.DictionaryBase mapping is provided mainly for compatibility with Ice versions prior to 3.3) On this page: Mapping to Predefined Containers for Dictionaries in C# mapping for Dictionaries in C#DictionaryBase Mapping to Predefined Containers for Dictionaries in C# Here is the definition of our once more:EmployeeMap Slice dictionary EmployeeMap; By default, the Slice-to-C# compiler maps the dictionary to the following type: C# System.Collections.Generic.Dictionary You can use the metadata directive to change the mapping to a sorted dictionary:"clr:generic:SortedDictionary" Slice ["clr:generic:SortedDictionary"] dictionary EmployeeMap; With this definition, the type of the dictionary becomes: C# System.Collections.Generic.SortedDictionary DictionaryBase mapping for Dictionaries in C# The mapping is provided mainly for compatibility with Ice versions prior to 3.3. Internally, isDictionaryBase DictionaryBase implemented using , so it offers the same performance trade-offs as System.Collections.Generic.Dictionary Dictionary . (For value types, is considerably faster than , however.)Ice.DictionaryBase System.Collections.DictionaryBase Ice.DictionaryBase is not as type-safe as because, in order to remain source code compatible with Dictionary , it provides methods that accept elements of type . This means that, if you pass anSystem.Collections.DictionaryBase object element of the wrong type, the problem will be diagnosed only at run time, instead of at compile time. For this reason, we suggest that you do not use the mapping for new code.DictionaryBase To enable the mapping, you must use the metadata directive:DictionaryBase "clr:collection" Slice ["clr:collection"] dictionary EmployeeMap;Ice 3.4.2 Documentation 428 Copyright © 2011, ZeroC, Inc. With this directive, generates a type that derives from :slice2cs Ice.CollectionBase C# public class EmployeeMap : Ice.DictionaryBase, System.ICloneable { public void AddRange(EmployeeMap m); public object Clone(); } Note that the generated class derives from , which provides a super-set of the interface of the .NET EmployeeMap Ice.DictionaryBase class. Apart from methods inherited from , the class provides a System.Collections.DictionaryBase DictionaryBase Clone method and an method that allows you to append the contents of one dictionary to another. If the target dictionary contains a keyAddRange that is also in the source dictionary, the target dictionary's value is preserved. For example: C# Employee e1 = new Employee(); e1.number = 42; e1.firstName = "Herb"; e1.lastName = "Sutter"; EmployeeMap em1 = new EmployeeMap(); em[42] = e; Employee e2 = new Employee(); e2.number = 42; e2.firstName = "Stan"; e2.lastName = "Lipmann"; EmployeeMap em2 = new EmployeeMap(); em[42] = e2; // Add contents of em2 to em1 // em1.AddRange(em2); // Equal keys preserve the original value // Debug.Assert(em1[42].firstName.Equals("Herb")); The class provides the following methods:DictionaryBaseIce 3.4.2 Documentation 429 Copyright © 2011, ZeroC, Inc. C# public abstract class DictionaryBase : System.Collections.IDictionary { public DictionaryBase(); public int Count { get; } public void Add(KT key, VT value); public void Add(object key, object value); public void CopyTo(System.Array a, int index); public void Remove(KT key); public void Remove(object key); public void Clear(); public System.Collections.ICollection Keys { get; } public System.Collections.ICollection Values { get; } public VT this[KT key] { get; set; } public object this[object key] { get; set; } public bool Contains(KT key); public bool Contains(object key); public override int GetHashCode(); public override bool Equals(object other); public static bool operator==(DictionaryBase lhs, DictionaryBase rhs); public static bool operator!=(DictionaryBase lhs, DictionaryBase rhs); public System.Collections.IEnumerator GetEnumerator(); public bool IsFixedSize { get; } public bool IsReadOnly { get; } public bool IsSynchronized { get; } public object SyncRoot { get; } } The methods have the same semantics as the corresponding methods in the .NET Framework. The method returns true if twoEquals dictionaries contain the same number of entries and, for each entry, the key and value are the same (as determined by their Equals methods). The method performs a shallow copy.Clone The class also implements the inherited , , and properties (which return false), and the IsFixedSize IsReadOnly IsSynchronized property (which returns ).SyncRoot this See Also Metadata C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Collection Comparison C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 430 Copyright © 2011, ZeroC, Inc. C-Sharp Collection Comparison The utility class allows you to compare collections for equality:Ice.CollectionComparer C# public class CollectionComparer { public static bool Equals(System.Collections.IDictionary d1, System.Collections.IDictionary d2); public static bool Equals(System.Collections.ICollection c1, System.Collections.ICollection c2); public static bool Equals(System.Collections.IEnumerable c1, System.Collections.IEnumerable c2); } Equality of the elements in a collection is determined by calling the elements' method.Equals Two dictionaries are equal if they contain the same number of entries with identical keys and values. Two collections that derive from or are equal if they contain the same number of entries and entries compareICollection IEnumerable equal. Note that order is significant, so corresponding entries must not only be equal but must also appear in the same position. See Also C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Mapping for Constants C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 431 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Constants Here are the sample once more:constant definitions Slice const bool AppendByDefault = true; const byte LowerNibble = 0x0f; const string Advice = "Don't Panic!"; const short TheAnswer = 42; const double PI = 3.1416; enum Fruit { Apple, Pear, Orange }; const Fruit FavoriteFruit = Pear; Here are the generated definitions for these constants: C# public abstract class AppendByDefault { public const bool value = true; } public abstract class LowerNibble { public const byte value = 15; } public abstract class Advice { public const string value = "Don't Panic!"; } public abstract class TheAnswer { public const short value = 42; } public abstract class PI { public const double value = 3.1416; } public enum Fruit { Apple, Pear, Orange } public abstract class FavoriteFruit { public const Fruit value = Fruit.Pear; } As you can see, each Slice constant is mapped to a class with the same name as the constant. The class contains a member named value that holds the value of the constant. The mapping to classes instead of to plain constants is necessary because C# does not permit constant definitions at namespace scope. See Also Constants and LiteralsIce 3.4.2 Documentation 432 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for ExceptionsIce 3.4.2 Documentation 433 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Exceptions On this page: Inheritance Hierarchy for Exceptions in C# C# Mapping for User Exceptions C# Default Constructors for User Exceptions C# Mapping for Run-Time Exceptions Inheritance Hierarchy for Exceptions in C# The mapping for exceptions is based on the inheritance hierarchy shown below: Inheritance structure for exceptions. The ancestor of all exceptions is . Derived from that is , which provides the definitions of a number ofSystem.Exception Ice.Exception constructors. and are derived from and form the base of all run-time andIce.LocalException Ice.UserException Ice.Exception user exceptions, respectively. The constructors defined in have the following signatures:Ice.Exception C# public abstract class Exception : System.Exception { public Exception(); public Exception(System.Exception ex); } Each concrete derived exception class implements these constructors. The second constructor initializes the property of InnerException . (Both constructors set the property to the empty string.)System.Exception Message C# Mapping for User ExceptionsIce 3.4.2 Documentation 434 Copyright © 2011, ZeroC, Inc. Here is a fragment of the once more:Slice definition for our world time server Slice exception GenericError { string reason; }; exception BadTimeVal extends GenericError {}; exception BadZoneName extends GenericError {}; These exception definitions map as follows: C# public partial class GenericError : Ice.UserException { public string reason; public GenericError(); public GenericError(System.Exception ex__); public GenericError(string reason); public GenericError(string reason, System.Exception ex__); // GetHashCode and comparison methods defined here, // as well as mapping-internal methods. } public partial class BadTimeVal : M.GenericError { public BadTimeVal(); public BadTimeVal(System.Exception ex__); public BadTimeVal(string reason); public BadTimeVal(string reason, System.Exception ex__); // GetHashCode and comparison methods defined here, // as well as mapping-internal methods. } public partial class BadZoneName : M.GenericError { public BadZoneName(); public BadZoneName(System.Exception ex__); public BadZoneName(string reason); public BadZoneName(string reason, System.Exception ex__); // GetHashCode and comparison methods defined here, // as well as mapping-internal methods. } Each Slice exception is mapped to a C# partial class with the same name. For each exception member, the corresponding class contains a public data member. (Obviously, because and do not have members, the generated classes for theseBadTimeVal BadZoneName exceptions also do not have members.) The inheritance structure of the Slice exceptions is preserved for the generated classes, so and inherit from BadTimeVal BadZoneName .GenericError All user exceptions are derived from the base class . This allows you to catch all user exceptions generically byIce.UserException installing a handler for . Similarly, you can catch all Ice run-time exceptions with a handler for Ice.UserException Ice.LocalException , and you can catch all Ice exceptions with a handler for .Ice.Exception All exceptions provide the usual and methods, as well as the and comparison operators.GetHashCode Equals == !=Ice 3.4.2 Documentation 435 Copyright © 2011, ZeroC, Inc. The generated exception classes also contain other member functions that are not shown here; these member functions are internal to the C# mapping and are not meant to be called by application code. C# Default Constructors for User Exceptions Exceptions have a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize a member whose type is a class-mapped structure because the Ice run time does not accept null as a legal value for these types. If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition Exceptions also provide constructors that accept one parameter for each data member. This allows you to construct and initialize a class instance in a single statement (instead of first having to construct the instance and then assign to its members). For derived exceptions, these constructors accept one argument for each base exception member, plus one argument for each derived exception member, in base-to-derived order. C# Mapping for Run-Time Exceptions The Ice run time throws run-time exceptions for a number of pre-defined error conditions. All run-time exceptions directly or indirectly derive from (which, in turn, derives from ).Ice.LocalException Ice.Exception Ice.LocalException implements a method that is inherited by its derived exceptions, so you can make memberwise shallowClone copies of exceptions. By catching exceptions at the appropriate point in the inheritance hierarchy, you can handle exceptions according to the category of error they indicate: Ice.Exception This is the root of the inheritance tree for both run-time and user exceptions. Ice.LocalException This is the root of the inheritance tree for run-time exceptions. Ice.UserException This is the root of the inheritance tree for user exceptions. Ice.TimeoutException This is the base exception for both operation-invocation and connection-establishment timeouts. Ice.ConnectTimeoutException This exception is raised when the initial attempt to establish a connection to a server times out. For example, a can be handled as , , ConnectTimeoutException ConnectTimeoutException TimeoutException , or .LocalException Exception You will probably have little need to catch run-time exceptions as their most-derived type and instead catch them as ; theLocalException fine-grained error handling offered by the remainder of the hierarchy is of interest mainly in the implementation of the Ice run time. Exceptions to this rule are the exceptions related to and life cycles, which you may want to catch explicitly. These exceptions arefacet object and , respectively.FacetNotExistException ObjectNotExistException See Also User Exceptions Run-Time Exceptions C-Sharp Mapping for Identifiers C-Sharp Mapping for Modules C-Sharp Mapping for Built-In Types C-Sharp Mapping for Enumerations C-Sharp Mapping for Structures C-Sharp Mapping for Sequences C-Sharp Mapping for Dictionaries C-Sharp Collection Comparison C-Sharp Mapping for Constants Facets and Versioning Object Life CycleIce 3.4.2 Documentation 436 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Interfaces The mapping of Slice revolves around the idea that, to invoke a remote operation, you call a member function on a local classinterfaces instance that is a for the remote object. This makes the mapping easy and intuitive to use because making a remote procedure call isproxy no different from making a local procedure call (apart from error semantics). On this page: Proxy Interfaces in C# The Interface in C#Ice.ObjectPrx Proxy Helpers in C# Using Proxy Methods in C# Object Identity and Proxy Comparison in C# Proxy Interfaces in C# On the client side, a Slice interface maps to a C# interface with member functions that correspond to the operations on that interface. Consider the following simple interface: Slice interface Simple { void op(); }; The Slice compiler generates the following definition for use by the client: C# public interface SimplePrx : Ice.ObjectPrx { void op(); void op(System.Collections.Generic.Dictionary __context); } As you can see, the compiler generates a . In general, the generated name is . If anproxy interface SimplePrx < >Prxinterface-name interface is nested in a module , the generated interface is part of namespace , so the fully-qualified name is .M M M.< >Prxinterface-name In the client's address space, an instance of is the local ambassador for a remote instance of the interface in a serverSimplePrx Simple and is known as a . All the details about the server-side object, such as its address, what protocol to use, and its objectproxy instance identity are encapsulated in that instance. Note that inherits from . This reflects the fact that all Ice interfaces implicitly inherit from .SimplePrx Ice.ObjectPrx Ice::Object For each operation in the interface, the proxy class has a member function of the same name. For the preceding example, we find that the operation has been mapped to the method . Also note that is overloaded: the second version of has a parameter ,op op op op __context which is a dictionary of string pairs. This parameter is for use by the Ice run time to store information about how to deliver a request. You normally do not need to use it. (We examine the parameter in detail in . The parameter is also used by __context Request Contexts .)IceStorm Because all the types are interfaces, you cannot instantiate an object of such a type. Instead, proxy instances are< >Prxinterface-name always instantiated on behalf of the client by the Ice run time, so client code never has any need to instantiate a proxy directly.The proxy references handed out by the Ice run time are always of type ; the concrete implementation of the interface is part< >Prxinterface-name of the Ice run time and does not concern application code. A value of denotes the null proxy. The null proxy is a dedicated value that indicates that a proxy points "nowhere" (denotes no object).null The Interface in C#Ice.ObjectPrx All Ice objects have as the ultimate ancestor type, so all proxies inherit from . provides a number ofObject Ice.ObjectPrx ObjectPrx methods:Ice 3.4.2 Documentation 437 Copyright © 2011, ZeroC, Inc. C# namespace Ice { public interface ObjectPrx { Identity ice_getIdentity(); bool ice_isA(string id); string ice_id(); void ice_ping(); int GetHashCode(); bool Equals(object r); // Defined in a helper class: // public static bool Equals(Ice.ObjectPrx lhs, ObjectPrx rhs); public static bool operator==(ObjectPrx lhs, ObjectPrx rhs); public static bool operator!=(ObjectPrx lhs, ObjectPrx rhs); // ... } } Note that the static methods are not actually defined in , but in a helper class that becomes a base class of an instantiatedIce.ObjectPrx proxy. However, this is simply an internal detail of the C# mapping — conceptually, these methods belong with , so weIce.ObjectPrx discuss them here. The methods behave as follows: ice_getIdentity This method returns the identity of the object denoted by the proxy. The identity of an Ice object has the following Slice type: Slice module Ice { struct Identity { string name; string category; }; }; To see whether two proxies denote the same object, first obtain the identity for each object and then compare the identities: C# Ice.ObjectPrx o1 = ...; Ice.ObjectPrx o2 = ...; Ice.Identity i1 = o1.ice_getIdentity(); Ice.Identity i2 = o2.ice_getIdentity(); if (i1.Equals(i2)) // o1 and o2 denote the same object else // o1 and o2 denote different objects ice_isA The method determines whether the object denoted by the proxy supports a specific interface. The argument to ice_isA ice_isA is a . For example, to see whether a proxy of type denotes a object, we can write:type ID ObjectPrx PrinterIce 3.4.2 Documentation 438 Copyright © 2011, ZeroC, Inc. C# Ice.ObjectPrx o = ...; if (o != null && o.ice_isA("::Printer")) // o denotes a Printer object else // o denotes some other type of object Note that we are testing whether the proxy is null before attempting to invoke the method. This avoids getting a ice_isA if the proxy is null.NullReferenceException ice_ids The method returns an array of strings representing all of the that the object denoted by the proxy supports.ice_ids type IDs ice_id The method returns the of the object denoted by the proxy. Note that the type returned is the type of the actualice_id type ID object, which may be more derived than the static type of the proxy. For example, if we have a proxy of type , with a staticBasePrx type ID of , the return value of might be , or it might something more derived, such as .::Base ice_id ::Base ::Derived ice_ping The method provides a basic reachability test for the object. If the object can physically be contacted (that is, the objectice_ping exists and its server is running and reachable), the call completes normally; otherwise, it throws an exception that indicates why the object could not be reached, such as or .ObjectNotExistException ConnectTimeoutException Equals This method compares two proxies for equality. Note that all aspects of proxies are compared by this operation, such as the communication endpoints for the proxy. This means that, in general, if two proxies compare unequal, that does imply that theynot denote different objects. For example, if two proxies denote the same Ice object via different transport endpoints, returns equals even though the proxies denote the same object.false The , , , and methods are remote operations and therefore support an additional overloading thatice_isA ice_ids ice_id ice_ping accepts a . Also note that there are in , not shown here. These methods provide different ways torequest context other methods ObjectPrx dispatch a call and also provide access to an object's .facets Proxy Helpers in C# For each Slice interface, apart from the proxy interface, the Slice-to-C# compiler creates a helper class: for an interface , the name ofSimple the generated helper class is .SimplePrxHelper You can ignore the base class — it exists for mapping-internal purposes.ObjectPrxHelperBase The helper class contains two methods of interest: C# public class SimplePrxHelper : Ice.ObjectPrxHelperBase, SimplePrx { public static SimplePrx checkedCast(Ice.ObjectPrx b); public static SimplePrx checkedCast( Ice.ObjectPrx b, System.Collections.Generic.Dictionary ctx); public static SimplePrx uncheckedCast(Ice.ObjectPrx b) // ... } Both the and methods implement a down-cast: if the passed proxy is a proxy for an object of type ,checkedCast uncheckedCast Simple or a proxy for an object with a type derived from , the cast returns a non-null reference to a proxy of type ; otherwise, ifSimple SimplePrx the passed proxy denotes an object of a different type (or if the passed proxy is null), the cast returns a null reference. Given a proxy of any type, you can use a to determine whether the corresponding object supports a given type, for example:checkedCastIce 3.4.2 Documentation 439 Copyright © 2011, ZeroC, Inc. C# Ice.ObjectPrx obj = ...; // Get a proxy from somewhere... SimplePrx simple = SimplePrxHelper.checkedCast(obj); if (simple != null) // Object supports the Simple interface... else // Object is not of type Simple... Note that a contacts the server. This is necessary because only the implementation of an object in the server has definitecheckedCast knowledge of the type of an object. As a result, a may throw a or an checkedCast ConnectTimeoutException . (This also explains the need for the helper class: the Ice run time must contact the server, so we cannot useObjectNotExistException a C# down-cast.) In contrast, an does not contact the server and unconditionally returns a proxy of the requested type. However, if you douncheckedCast use an , you must be certain that the proxy really does support the type you are casting to; otherwise, if you get it wrong,uncheckedCast you will most likely get a run-time exception when you invoke an operation on the proxy. The most likely error for such a type mismatch is . However, other exceptions, such as a marshaling exception are possible as well. And, if the objectOperationNotExistException happens to have an operation with the correct name, but different parameter types, no exception may be reported at all and you simply end up sending the invocation to an object of the wrong type; that object may do rather nonsensical things. To illustrate this, consider the following two interfaces: Slice interface Process { void launch(int stackSize, int dataSize); }; // ... interface Rocket { void launch(float xCoord, float yCoord); }; Suppose you expect to receive a proxy for a object and use an to down-cast the proxy:Process uncheckedCast C# Ice.ObjectPrx obj = ...; // Get proxy... ProcessPrx process = ProcessPrxHelper.uncheckedCast(obj); // No worries... process.launch(40, 60); // Oops... If the proxy you received actually denotes a object, the error will go undetected by the Ice run time: because and haveRocket int float the same size and because the Ice protocol does not tag data with its type on the wire, the implementation of will simplyRocket::launch misinterpret the passed integers as floating-point numbers. In fairness, this example is somewhat contrived. For such a mistake to go unnoticed at run time, both objects must have an operation with the same name and, in addition, the run-time arguments passed to the operation must have a total marshaled size that matches the number of bytes that are expected by the unmarshaling code on the server side. In practice, this is extremely rare and an incorrect uncheckedCast typically results in a run-time exception. A final warning about down-casts: you must use either a or an to down-cast a proxy. If you use a C# cast,checkedCast uncheckedCast the behavior is undefined. Using Proxy Methods in C# The base proxy class supports a variety of methods for . Since proxies are immutable, each of theseObjectPrx customizing a proxy "factory methods" returns a copy of the original proxy that contains the desired modification. For example, you can obtain a proxy configured with a ten second timeout as shown below:Ice 3.4.2 Documentation 440 Copyright © 2011, ZeroC, Inc. C# Ice.ObjectPrx proxy = communicator.stringToProxy(...); proxy = proxy.ice_timeout(10000); A factory method returns a new proxy object if the requested modification differs from the current proxy, otherwise it returns the current proxy. With few exceptions, factory methods return a proxy of the same type as the current proxy, therefore it is generally not necessary to repeat a or after using a factory method. However, a regular cast is still required, as shown in the examplecheckedCast uncheckedCast below: C# Ice.ObjectPrx base = communicator.stringToProxy(...); HelloPrx hello = HelloPrxHelper.checkedCast(base); hello = (HelloPrx)hello.ice_timeout(10000); # Type is preserved hello.sayHello(); The only exceptions are the factory methods and . Calls to either of these methods may produce a proxy for anice_facet ice_identity object of an unrelated type, therefore they return a base proxy that you must subsequently down-cast to an appropriate type. Object Identity and Proxy Comparison in C# Proxies provide an method that compares proxies:Equals C# public interface ObjectPrx { bool Equals(object r); } Note that proxy comparison with uses of the information in a proxy for the comparison. This means that not only the objectEquals all identity must match for a comparison to succeed, but other details inside the proxy, such as the protocol and endpoint information, must be the same. In other words, comparison with (or and ) tests for identity, object identity. A common mistake is to writeEquals == != proxy not code along the following lines: C# Ice.ObjectPrx p1 = ...; // Get a proxy... Ice.ObjectPrx p2 = ...; // Get another proxy... if (p1.Equals(p2)) { // p1 and p2 denote different objects // WRONG! } else { // p1 and p2 denote the same object // Correct } Even though and differ, they may denote the same Ice object. This can happen because, for example, both and embed thep1 p2 p1 p2 same object identity, but each use a different protocol to contact the target object. Similarly, the protocols may be the same, but denote different endpoints (because a single Ice object can be contacted via several different transport endpoints). In other words, if two proxies compare equal with , we know that the two proxies denote the same object (because they are identical in all respects); however, ifEquals two proxies compare unequal with , we know absolutely nothing: the proxies may or may not denote the same object.Equals To compare the object identities of two proxies, you can use a helper function in the class:Ice.UtilIce 3.4.2 Documentation 441 Copyright © 2011, ZeroC, Inc. C# public sealed class Util { public static int proxyIdentityCompare(ObjectPrx lhs, ObjectPrx rhs); public static int proxyIdentityAndFacetCompare(ObjectPrx lhs, ObjectPrx rhs); // ... proxyIdentityCompare allows you to correctly compare proxies for identity: C# Ice.ObjectPrx p1 = ...; // Get a proxy... Ice.ObjectPrx p2 = ...; // Get another proxy... if (Ice.Util.proxyIdentityCompare(p1, p2) != 0) { // p1 and p2 denote different objects // Correct } else { // p1 and p2 denote the same object // Correct } The function returns 0 if the identities are equal, if is less than , and 1 if is greater than . (The comparison uses as the-1 p1 p2 p1 p2 name major and as the minor sort key.)category The function behaves similarly, but compares both the identity and the .proxyIdentityAndFacetCompare facet name The C# mapping also provides two helper classes in the namespace that allow you to insert proxies into hashtables or orderedIce collections, based on the identity, or the identity plus the facet name: C# public class ProxyIdentityKey : System.Collections.IHashCodeProvider, System.Collections.IComparer { public int GetHashCode(object obj); public int Compare(object obj1, object obj2); } public class ProxyIdentityFacetKey : System.Collections.IHashCodeProvider, System.Collections.IComparer { public int GetHashCode(object obj); public int Compare(object obj1, object obj2); } Note these classes derive from and , so they can be used for both hash tables and ordered collections.IHashCodeProvider IComparer See Also Interfaces, Operations, and Exceptions Proxies C-Sharp Mapping for Operations Operations on Object Proxy Methods Facets and Versioning IceStormIce 3.4.2 Documentation 442 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Operations On this page: Basic C# Mapping for Operations Normal and Operations in C#idempotent Passing Parameters in C# In-Parameters in C# Out-Parameters in C# Null Parameters in C# Exception Handling in C# Exceptions and Out-Parameters in C# Basic C# Mapping for Operations As we saw in the , for each on an interface, the proxy class contains a corresponding member functionC# mapping for interfaces operation with the same name. To invoke an operation, you call it via the proxy. For example, here is part of the definitions for our :file system Slice module Filesystem { interface Node { idempotent string name(); }; // ... }; The operation returns a value of type . Given a proxy to an object of type , the client can invoke the operation as follows:name string Node C# NodePrx node = ...; // Initialize proxy string name = node.name(); // Get name via RPC This illustrates the typical pattern for receiving return values: return values are returned by reference for complex types, and by value for simple types (such as or ).int double Normal and Operations in C#idempotent You can add an qualifier to a Slice operation. As far as the signature for the corresponding proxy method is concerned, idempotent has no effect. For example, consider the following interface:idempotent Slice interface Example { string op1(); idempotent string op2(); }; The proxy interface for this is: C# public interface ExamplePrx : Ice.ObjectPrx { string op1(); string op2(); } Because affects an aspect of call dispatch, not interface, it makes sense for the two methods to be mapped the same.idempotentIce 3.4.2 Documentation 443 Copyright © 2011, ZeroC, Inc. Passing Parameters in C# In-Parameters in C# The parameter passing rules for the C# mapping are very simple: parameters are passed either by value (for value types) or by reference (for reference types). Semantically, the two ways of passing parameters are identical: it is guaranteed that the value of a parameter will not be changed by the invocation (with some caveats — see ).Location Transparency Here is an interface with operations that pass parameters of various types from client to server: Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ClientToServer { void op1(int i, float f, bool b, string s); void op2(NumberAndString ns, StringSeq ss, StringTable st); void op3(ClientToServer* proxy); }; The Slice compiler generates the following proxy for these definitions: C# public interface ClientToServerPrx : Ice.ObjectPrx { void op1(int i, float f, bool b, string s); void op2(NumberAndString ns, string[] ss, Dictionary st); void op3(ClientToServerPrx proxy); } Given a proxy to a interface, the client code can pass parameters as in the following example:ClientToServerIce 3.4.2 Documentation 444 Copyright © 2011, ZeroC, Inc. C# ClientToServerPrx p = ...; // Get proxy... p.op1(42, 3.14f, true, "Hello world!"); // Pass simple literals int i = 42; float f = 3.14f; bool b = true; string s = "Hello world!"; p.op1(i, f, b, s); // Pass simple variables NumberAndString ns = new NumberAndString(); ns.x = 42; ns.str = "The Answer"; string[] ss = new string[1]; ss[0] = "Hello world!"; Dictionary st = new Dictionary(); st[0] = ss; p.op2(ns, ss, st); // Pass complex variables p.op3(p); // Pass proxy Out-Parameters in C# Slice parameters simply map to C# parameters.out out Here again are the same Slice definitions we saw earlier, but this time with all parameters being passed in the direction:out Slice struct NumberAndString { int x; string str; }; sequence StringSeq; dictionary StringTable; interface ServerToClient { void op1(out int i, out float f, out bool b, out string s); void op2(out NumberAndString ns, out StringSeq ss, out StringTable st); void op3(out ServerToClient* proxy); }; The Slice compiler generates the following code for these definitions: C# public interface ServerToClientPrx : Ice.ObjectPrx { void op1(out int i, out float f, out bool b, out string s); void op2(out NumberAndString ns, out string[] ss, out Dictionary st); void op3(out ServerToClientPrx proxy); } Given a proxy to a interface, the client code can pass parameters as in the following example:ServerToClientIce 3.4.2 Documentation 445 Copyright © 2011, ZeroC, Inc. C# ClientToServerPrx p = ...; // Get proxy... int i; float f; bool b; string s; p.op1(out i, out f, out b, out s); NumberAndString ns; string[] ss; Dictionary st; p.op2(out ns, out ss, out st); ServerToClientPrx stc; p.op3(out stc); System.Console.WriteLine(i); // Show one of the values Null Parameters in C# Some Slice types naturally have "empty" or "not there" semantics. Specifically, C# sequences (if mapped to ),CollectionBase dictionaries, strings, and structures (if mapped to ) all can be , but the corresponding Slice types do not have the concept of aclasses null null value. Slice sequences, dictionaries, and strings cannot be null, but can be empty. To make life with these types easier, whenever you pass a C# reference as a parameter or return value of type sequence, dictionary, or string, the Ice run time automaticallynull sends an empty sequence, dictionary, or string to the receiver. If you pass a C# reference to a Slice structure that is mapped to a C# as a parameter or return value, the Ice run timenull class automatically sends a structure whose elements are default-initialized. This means that all proxy members are initialized to ,null sequence and dictionary members are initialized to empty collections, strings are initialized to the empty string, and members that have a value type are initialized to their default values. This behavior is useful as a convenience feature: especially for deeply-nested data types, members that are structures, sequences, dictionaries, or strings automatically arrive as an empty value at the receiving end. This saves you having to explicitly initialize, for example, every string element in a large sequence before sending the sequence in order to avoid . Note that using nullNullReferenceException parameters in this way does create null semantics for Slice sequences, dictionaries, or strings. As far as the object model is concerned,not these do not exist (only sequences, dictionaries, and strings do). For example, whether you send a string as or as an emptyempty null string makes no difference to the receiver: either way, the receiver sees an empty string. Exception Handling in C# Any operation invocation may throw a and, if the operation has an exception specification, may also throw run-time exception user . Suppose we have the following simple interface:exceptions Slice exception Tantrum { string reason; }; interface Child { void askToCleanUp() throws Tantrum; }; Slice exceptions are thrown as C# exceptions, so you can simply enclose one or more operation invocations in a - block:try catchIce 3.4.2 Documentation 446 Copyright © 2011, ZeroC, Inc. C# ChildPrx child = ...; // Get child proxy... try { child.askToCleanUp(); } catch (Tantrum t) { System.Console.Write("The child says: "); System.Console.WriteLine(t.reason); } Typically, you will catch only a few exceptions of specific interest around an operation invocation; other exceptions, such as unexpected run-time errors, will typically be handled by exception handlers higher in the hierarchy. For example: C# public class Client { private static void run() { ChildPrx child = ...; // Get child proxy... try { child.askToCleanUp(); } catch (Tantrum t) { System.Console.Write("The child says: "); System.Console.WriteLine(t.reason); child.scold(); // Recover from error... } child.praise(); // Give positive feedback... } static void Main(string[] args) { try { // ... run(); // ... } catch (Ice.Exception e) { System.Console.WriteLine(e); } } } This code handles a specific exception of local interest at the point of call and deals with other exceptions generically. (This is also the strategy we used for our .)first simple application Note that the method of exceptions prints the name of the exception, any inner exceptions, and the stack trace. Of course, youToString can be more selective in the way exceptions are displayed. For example, returns the (unscoped) name of ane.GetType().Name exception. Exceptions and Out-Parameters in C# The Ice run time makes no guarantees about the state of out-parameters when an operation throws an exception: the parameter may still have its original value or may have been changed by the operation's implementation in the target object. In other words, for out-parameters, Ice provides the weak exception guarantee but does not provide the strong exception guarantee.[1]Ice 3.4.2 Documentation 447 Copyright © 2011, ZeroC, Inc. 1. This is done for reasons of efficiency: providing the strong exception guarantee would require more overhead than can be justified. See Also Operations C-Sharp Mapping for Exceptions C-Sharp Mapping for Interfaces Location Transparency References Sutter, H. 1999. . Reading, MA: Addison-Wesley.Exceptional C++: 47 Engineering Puzzles, Programming Problems, and SolutionsIce 3.4.2 Documentation 448 Copyright © 2011, ZeroC, Inc. C-Sharp Mapping for Classes On this page: Basic C# Mapping for Classes Operations Interfaces in C# Inheritance from in C#Ice.Object Class Data Members in C# Class Operations in C# Class Factories in C# Class Constructors in C# Basic C# Mapping for Classes A Slice is mapped to a C# class with the same name. By default, the generated class contains a public data member for each Sliceclass data member (just as for structures and exceptions), and a member function for each operation. Alternatively, you can use the property by specifying the metadata directive, which generates classes with virtual properties instead of data members.mapping "clr:property" Consider the following class definition: Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; The Slice compiler generates the following code for this definition:Ice 3.4.2 Documentation 449 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. C# public interface TimeOfDayOperations_ { string format(Ice.Current __current); } public interface TimeOfDayOperationsNC_ { string format(); } public abstract partial class TimeOfDay : Ice.ObjectImpl, TimeOfDayOperations_, TimeOfDayOperationsNC_ { public short hour; public short minute; public short second; public TimeOfDay() { } public TimeOfDay(short hour, short minute, short second) { this.hour = hour; this.minute = minute; this.second = second; } public string format() { return format(new Ice.Current()); } public abstract string format(Ice.Current __current); } There are a number of things to note about the generated code: The compiler generates "operations interfaces" called and _. TheseTimeOfDayOperations_ TimeOfDayOperationsNC interfaces contain a method for each Slice operation of the class. The generated class inherits (indirectly) from . This means that all classes implicitly inherit from TimeOfDay Ice.Object , which is the ultimate ancestor of all classes. Note that is the same as . In otherIce.Object Ice.Object not Ice.ObjectPrx words, you pass a class where a proxy is expected and vice versa. cannot If a class has only data members, but no operations, the compiler generates a non-abstract class. The generated class contains a public member for each Slice data member. The generated class inherits member functions for each Slice operation from the operations interfaces. The generated class contains two constructors. There is quite a bit to discuss here, so we will look at each item in turn. Operations Interfaces in C# The methods in the interface have an additional trailing parameter of type , whereas theOperations_ Ice.Current methods in the interface lack this additional trailing parameter. The methods without the OperationsNC_ Current parameter simply forward to the methods with a parameter, supplying a default . For now, you can ignore this parameterCurrent Current and pretend it does not exist. If a class has only data members, but no operations, the compiler omits generating the and Operations_ interfaces.OperationsNC_Ice 3.4.2 Documentation 450 Copyright © 2011, ZeroC, Inc. Inheritance from in C#Ice.Object Like interfaces, classes implicitly inherit from a common base class, . However, as shown in the illustration below, classesIce.Object inherit from instead of (which is at the base of the inheritance hierarchy for proxies). As a result, you cannotIce.Object Ice.ObjectPrx pass a class where a proxy is expected (and vice versa) because the base types for classes and proxies are not compatible. Inheritance from and .Ice.ObjectPrx Ice.Object Ice.Object contains a number of member functions: C# namespace Ice { public interface Object : System.ICloneable { bool ice_isA(string s); bool ice_isA(string s, Current current); void ice_ping(); void ice_ping(Current current); string[] ice_ids(); string[] ice_ids(Current current); string ice_id(); string ice_id(Current current); void ice_preMarshal(); void ice_postUnmarshal(); DispatchStatus ice_dispatch(Request request, DispatchInterceptorAsyncCallback cb); } } The member functions of behave as follows:Ice.Object ice_isA This function returns if the object supports the given , and otherwise.true type ID false ice_ping As for interfaces, provides a basic reachability test for the class.ice_ping ice_ids This function returns a string sequence representing all of the supported by this object, including .type IDs ::Ice::Object ice_id This function returns the actual run-time for a class. If you call through a reference to a base instance, the returnedtype ID ice_id type id is the actual (possibly more derived) type ID of the instance. ice_preMarshal The Ice run time invokes this function prior to marshaling the object's state, providing the opportunity for a subclass to validate its declared data members.Ice 3.4.2 Documentation 451 Copyright © 2011, ZeroC, Inc. ice_postUnmarshal The Ice run time invokes this function after unmarshaling an object's state. A subclass typically overrides this function when it needs to perform additional initialization using the values of its declared data members. ice_dispatch This function dispatches an incoming request to a servant. It is used in the implementation of .dispatch interceptors Note that the generated class does override and . This means that classes are compared using shallow referencenot GetHashCode Equals equality, not value equality (as is used for structures). The class also provides a method (whose implementation is inherited from ); the method returns a shallowClone Ice.ObjectImpl Clone memberwise copy. Class Data Members in C# By default, data members of classes are mapped exactly as for structures and exceptions: for each data member in the Slice definition, the generated class contains a corresponding public data member. If you wish to restrict access to a data member, you can modify its visibility using the metadata directive. The presence of thisprotected directive causes the Slice compiler to generate the data member with protected visibility. As a result, the member can be accessed only by the class itself or by one of its subclasses. For example, the class shown below has the metadata directive appliedTimeOfDay protected to each of its data members: Slice class TimeOfDay { ["protected"] short hour; // 0 - 23 ["protected"] short minute; // 0 - 59 ["protected"] short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; The Slice compiler produces the following generated code for this definition: C# public abstract partial class TimeOfDay : Ice.ObjectImpl, TimeOfDayOperations_, TimeOfDayOperationsNC_ { protected short hour; protected short minute; protected short second; public TimeOfDay() { } public TimeOfDay(short hour, short minute, short second) { this.hour = hour; this.minute = minute; this.second = second; } // ... } For a class in which all of the data members are protected, the metadata directive can be applied to the class itself rather than to each member individually. For example, we can rewrite the class as follows:TimeOfDayIce 3.4.2 Documentation 452 Copyright © 2011, ZeroC, Inc. Slice ["protected"] class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; If a protected data member also has the directive, the generated property has protected visibility. Consider the clr:property TimeOfDay class once again: Slice ["protected", "clr:property"] class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 string format(); // Return time as hh:mm:ss }; The effects of combining these two metadata directives are shown in the generated code below: C# public abstract partial class TimeOfDay : Ice.ObjectImpl, TimeOfDayOperations_, TimeOfDayOperationsNC_ { private short hour_prop; protected short hour { get { return hour_prop; } set { hour_prop = value; } } // ... } Refer to the for more information on the property mapping for data members.structure mapping Class Operations in C# Operations of classes are mapped to abstract member functions in the generated class. This means that, if a class contains operations (such as the operation of our class), you must provide an implementation of the operation in a class that is derived from theformat TimeOfDay generated class. For example:Ice 3.4.2 Documentation 453 Copyright © 2011, ZeroC, Inc. C# public class TimeOfDayI : TimeOfDay { public string format(Ice.Current current) { return hour.ToString("D2") + ":" + minute.ToString("D2") + ":" + second.ToString("D2"); } } Class Factories in C# Having created a class such as , we have an implementation and we can instantiate the class, but we cannotTimeOfDayI TimeOfDayI receive it as the return value or as an out-parameter from an operation invocation. To see why, consider the following simple interface: Slice interface Time { TimeOfDay get(); }; When a client invokes the operation, the Ice run time must instantiate and return an instance of the class. However, get TimeOfDay is an abstract class that cannot be instantiated. Unless we tell it, the Ice run time cannot magically know that we have created a TimeOfDay class that implements the abstract operation of the abstract class. In other words, we must provide theTimeOfDayI format TimeOfDay Ice run time with a factory that knows that the abstract class has a concrete implementation. The TimeOfDay TimeOfDayI interface provides us with the necessary operations:Ice::Communicator Slice module Ice { local interface ObjectFactory { Object create(string type); void destroy(); }; local interface Communicator { void addObjectFactory(ObjectFactory factory, string id); ObjectFactory findObjectFactory(string id); // ... }; }; To supply the Ice run time with a factory for our class, we must implement the interface:TimeOfDayI ObjectFactoryIce 3.4.2 Documentation 454 Copyright © 2011, ZeroC, Inc. C# class ObjectFactory : Ice.ObjectFactory { public Ice.Object create(string type) { if (type.Equals(M.TimeOfDay.ice_staticId())) return new TimeOfDayI(); System.Diagnostics.Debug.Assert(false); return null; } public void destroy() { // Nothing to do } } The object factory's method is called by the Ice run time when it needs to instantiate a class. The factory's create TimeOfDay destroy method is called by the Ice run time when its communicator is destroyed. The method is passed the of the class to instantiate. For our class, the type ID is . Ourcreate type ID TimeOfDay "::M::TimeOfDay" implementation of checks the type ID: if it matches, the method instantiates and returns a object. For other type IDs,create TimeOfDayI the method asserts because it does not know how to instantiate other types of objects. Note that we used the method to obtain the type ID rather than embedding a literal string. Using a literal type ID string inice_staticId your code is discouraged because it can lead to errors that are only detected at run time. For example, if a Slice class or one of its enclosing modules is renamed and the literal string is not changed accordingly, a receiver will fail to unmarshal the object and the Ice run time will raise . By using instead, we avoid any risk of a misspelled or obsolete type ID, and we canNoObjectFactoryException ice_staticId discover at compile time if a Slice class or module has been renamed. Given a factory implementation, such as our , we must inform the Ice run time of the existence of the factory:ObjectFactory C# Ice.Communicator ic = ...; ic.addObjectFactory(new ObjectFactory(), M.TimeOfDay.ice_staticId()); Now, whenever the Ice run time needs to instantiate a class with the type ID , it calls the method of the"::M::TimeOfDay" create registered instance, which returns a instance to the Ice run time.ObjectFactory TimeOfDayI The operation of the object factory is invoked by the Ice run time when the communicator is destroyed. This gives you a chance todestroy clean up any resources that may be used by your factory. Do not call on the factory while it is registered with the communicator —destroy if you do, the Ice run time has no idea that this has happened and, depending on what your implementation is doing, may causedestroy undefined behavior when the Ice run time tries to next use the factory. The run time guarantees that will be the last call made on the factory, that is, will not be called concurrently with destroy create destroy , and will not be called once has been called. However, calls to can be made concurrently.create destroy create Note that you cannot register a factory for the same type ID twice: if you call with a type ID for which a factory isaddObjectFactory registered, the Ice run time throws an .AlreadyRegisteredException Finally, keep in mind that if a class has only data members, but no operations, you need not create and register an object factory to transmit instances of such a class. Only if a class has operations do you have to define and register an object factory. Class Constructors in C# Classes have a default constructor that default-constructs each data member. This means members of primitive type are initialized to the equivalent of zero, and members of reference type are initialized to null. Note that applications must always explicitly initialize a member whose type is a because the Ice run time does not accept null as a legal value for these types.class-mapped structure If you wish to ensure that data members of primitive and enumerated types are initialized to specific values, you can declare default values in your . The default constructor initializes each of these data members to its declared value.Slice definition Classes also provide a constructor that accepts one argument for each member of the class. This allows you to create and initialize a classIce 3.4.2 Documentation 455 Copyright © 2011, ZeroC, Inc. in a single statement, for example: C# TimeOfDayI tod = new TimeOfDayI(14, 45, 00); // 2:45pm For derived classes, the constructor requires one argument for every member of the class, including inherited members. For example, consider the the definition from once more:Class Inheritance Slice class TimeOfDay { short hour; // 0 - 23 short minute; // 0 - 59 short second; // 0 - 59 }; class DateTime extends TimeOfDay { short day; // 1 - 31 short month; // 1 - 12 short year; // 1753 onwards }; The constructors for the generated classes are as follows: C# public partial class TimeOfDay : Ice.ObjectImpl { public TimeOfDay() {} public TimeOfDay(short hour, short minute, short second) { this.hour = hour; this.minute = minute; this.second = second; } // ... } public partial class DateTime : TimeOfDay { public DateTime() : base() {} public DateTime(short hour, short minute, short second, short day, short month, short year) : base(hour, minute, second) { this.day = day; this.month = month; this.year = year; } // ... } If you want to instantiate and initialize a instance, you must either use the default constructor or provide values for all of the dataDateTime members of the instance, including data members of any base classes.Ice 3.4.2 Documentation 456 Copyright © 2011, ZeroC, Inc. See Also Classes Class Inheritance Type IDs C-Sharp Mapping for Structures The Current Object Dispatch InterceptorsIce 3.4.2 Documentation 457 Copyright © 2011, ZeroC, Inc. Serializable Objects in C-Sharp In addition to serializing Slice types, applications may also need to incorporate foreign types into their Slice definitions. Ice allows you to pass CLR directly as operation parameters or as fields of another data type. For example:serializable objects Slice ["clr:serializable:SomeNamespace.CLRClass"] sequence CLRObj; struct MyStruct { int i; CLRObj o; }; interface Example { void op(CLRObj o, MyStruct s); }; The generated code for contains member of type and a member of type :MyStruct i int o SomeNamespace.CLRClass C# public partial class MyStruct : _System.ICloneable { public int i; SomeNamespace.CLRClass o; // ... } Similarly, the signature for has parameters of type and :op CLRClass MyStruct C# void op(SomeNamespace.CLRClass o, MyStruct s); Of course, your client and server code must have an implementation of that sets the attribute:CLRClass Serializable C# namespace SomeNamespace { [Serializable] public class CLRClass { // ... } } You can implement this class in any way you see fit — the Ice run time does not place any other requirements on the implementation. However, note that the CLR requires the class to reside in the same assembly for client and server. See Also Serializable ObjectsIce 3.4.2 Documentation 458 Copyright © 2011, ZeroC, Inc. C-Sharp Specific Metadata Directives The compiler supports directives that allow you to inject C# attribute specifications into the generated code. Theslice2cs metadata metadata directive is . For example:cs:attribute: Slice ["cs:attribute:System.Serializable"] struct Point { double x; double y; }; This results in the following code being generated for :S C# [System.Serializable] public partial struct Point { public double x; public double y; // ... } You can apply this metadata directive to any Slice construct, such as structure, operation, or parameter definitions. You can use this directive also at global level. For example: Slice [["cs:attribute:assembly: AssemblyDescription(\"My assembly\")"]] This results in the following code being inserted after any directives and before any definitions:using C# [assembly: AssemblyDescription("My assembly")] See Also Metadata Slice Metadata DirectivesIce 3.4.2 Documentation 459 Copyright © 2011, ZeroC, Inc. Asynchronous Method Invocation (AMI) in C-Sharp Asynchronous Method Invocation (AMI) is the term used to describe the client-side support for the asynchronous programming model. AMI supports both oneway and twoway requests, but unlike their synchronous counterparts, AMI requests never block the calling thread. When a client issues an AMI request, the Ice run time hands the message off to the local transport buffer or, if the buffer is currently full, queues the request for later delivery. The application can then continue its activities and poll or wait for completion of the invocation, or receive a callback when the invocation completes. AMI is transparent to the server: there is no way for the server to tell whether a client sent a request synchronously or asynchronously. As of version 3.4, Ice provides a new API for asynchronous method invocation. This page describes the new API. Note that the is deprecated and will be removed in a future release.old API On this page: Basic Asynchronous API in C# Asynchronous Proxy Methods in C# Asynchronous Exception Semantics in C# Class in C#AsyncResult Polling for Completion in C# Generic Completion Callbacks in C# Using Cookies for Generic Completion Callbacks in C# Type-Safe Completion Callbacks in C# Using Cookies for Type-Safe Completion Callbacks in C# Asynchronous Oneway Invocations in C# Flow Control in C# Asynchronous Batch Requests in C# Concurrency Semantics for AMI in C# AMI Limitations in C# Basic Asynchronous API in C# Consider the following simple Slice definition: Slice module Demo { interface Employees { string getName(int number); }; }; Asynchronous Proxy Methods in C# Besides the synchronous proxy methods, generates the following asynchronous proxy methods:slice2cs C# public interface EmployeesPrx : Ice.ObjectPrx { Ice.AsyncResult begin_getName(int number); Ice.AsyncResult begin_getName(int number, _System.Collections.Generic.Dictionary ctx__); string end_getName(Ice.AsyncResult r__); }Ice 3.4.2 Documentation 460 Copyright © 2011, ZeroC, Inc. Two additional overloads of are generated for use with .begin_getName generic completion callbacks As you can see, the single operation results in and methods. (The method is overloadedgetName begin_getName end_getName begin_ so you can pass a .)per-invocation context The method sends (or queues) an invocation of . This method does not block the calling thread.begin_getName getName The method collects the result of the asynchronous invocation. If, at the time the calling thread calls ,end_getName end_getName the result is not yet available, the calling thread blocks until the invocation completes. Otherwise, if the invocation completed some time before the call to , the method returns immediately with the result.end_getName A client could call these methods as follows: C# EmployeesPrx e = ...; Ice.AsyncResult r = e.begin_getName(99); // Continue to do other things here... string name = e.end_getName(r); Because does not block, the calling thread can do other things while the operation is in progress.begin_getName Note that returns a value of type . (The class derives from .) This valuebegin_getName Ice.AsyncResult System.IAsyncResult contains the state that the Ice run time requires to keep track of the asynchronous invocation. You must pass the that isAsyncResult returned by the method to the corresponding method.begin_ end_ The method has one parameter for each in-parameter of the corresponding Slice operation. Similarly, the method has onebegin_ end_ out-parameter for each out-parameter of the corresponding Slice operation (plus the parameter). For example, consider theAsyncResult following operation: Slice double op(int inp1, string inp2, out bool outp1, out long outp2); The and methods have the following signature:begin_op end_op C# Ice.AsyncResult begin_op(int inp1, string inp2); double end_op(out bool outp1, out long outp2, Ice.AsyncResult r__); Asynchronous Exception Semantics in C# If an invocation raises an exception, the exception is thrown by the method, even if the actual error condition for the exception wasend_ encountered during the method ("on the way out"). The advantage of this behavior is that all exception handling is located with thebegin_ code that calls the method (instead of being present twice, once where the method is called, and again where the end_ begin_ end_ method is called). There is one exception to the above rule: if you destroy the communicator and then make an asynchronous invocation, the methodbegin_ throws . This is necessary because, once the run time is finalized, it can no longer throw anCommunicatorDestroyedException exception from the method.end_ The only other exception that is thrown by the and methods is . This exception indicates thatbegin_ end_ System.ArgumentException you have used the API incorrectly. For example, the method throws this exception if you call an operation that has a return value orbegin_ out-parameters on a oneway proxy. Similarly, the method throws this exception if you use a different proxy to call the methodend_ end_ than the proxy you used to call the method, or if the you pass to the method was obtained by calling the begin_ AsyncResult end_ method for a different operation.begin_Ice 3.4.2 Documentation 461 Copyright © 2011, ZeroC, Inc. AsyncResult Class in C# The that is returned by the method encapsulates the state of the asynchronous invocation:AsyncResult begin_ C# public interface AsyncResult : System.IAsyncResult { Ice.Communicator getCommunicator(); Ice.Connection getConnection(); ObjectPrx getProxy(); string getOperation(); object AsyncState { get; } bool IsCompleted { get; } void waitForCompleted(); bool isSent(); void waitForSent(); void throwLocalException(); bool sentSynchronously(); AsyncResult whenSent(Ice.AsyncCallback cb); AsyncResult whenSent(Ice.SentCallback cb); AsyncResult whenCompleted(Ice.ExceptionCallback ex); } public interface AsyncResult : AsyncResult { AsyncResult whenCompleted(T cb, Ice.ExceptionCallback excb); new AsyncResult whenCompleted(Ice.ExceptionCallback excb); new AsyncResult whenSent(Ice.SentCallback cb); } The methods and properties have the following semantics: Communicator getCommunicator() This method returns the communicator that sent the invocation. Connection getConnection() This method returns the connection that was used for the invocation. ObjectPrx getProxy() This method returns the proxy that was used to call the method.begin_ string getOperation() This method returns the name of the operation. object AsyncState { get; } This property stores an object that you can use to pass shared state from the to the method.begin_ end_ bool IsCompleted { get; } This property is true if, at the time it is called, the result of an invocation is available, indicating that a call to the method will notend_ block the caller. Otherwise, if the result is not yet available, the method returns false. void waitForCompleted() This method blocks the caller until the result of an invocation becomes available. bool isSent() When you call the method, the Ice run time attempts to write the corresponding request to the client-side transport. If thebegin_ transport cannot accept the request, the Ice run time queues the request for later transmission. returns true if, at the time itisSent is called, the request has been written to the local transport (whether it was initially queued or not). Otherwise, if the request is still queued or an exception occurred before the request could be sent, returns false.isSentIce 3.4.2 Documentation 462 Copyright © 2011, ZeroC, Inc. void waitForSent() This method blocks the calling thread until a request has been written to the client-side transport, or an exception occurs. After returns, returns true if the request was successfully written to the client-side transport, or false if anwaitForSent isSent exception occurred. In the case of a failure, you can call the corresponding method or to obtain theend_ throwLocalException exception. void throwLocalException() This method throws the local exception that caused the invocation to fail. If no exception has occurred yet, throwLocalException does nothing. bool sentSynchronously() This method returns true if a request was written to the client-side transport without first being queued. If the request was initially queued, returns false (independent of whether the request is still in the queue or has since been written tosentSynchronously the client-side transport). AsyncResult whenSent(Ice.SentCallback cb) AsyncResult whenSent(Ice.SentCallback cb) AsyncResult whenCompleted(Ice.ExceptionCallback ex) AsyncResult whenCompleted(T cb,Ice.ExceptionCallback excb) AsyncResult whenCompleted(Ice.ExceptionCallback excb) These methods allow you to specify callback methods that are called by the Ice run time. The methods set a callbackwhenSent that triggers when an asynchronous invocation is written to the client-side transport. The methods set a callbackwhenCompleted that triggers when an asynchronous invocation completes (also see ).Generic Completion Callbacks in C# Polling for Completion in C# The methods allow you to poll for call completion. Polling is useful in a variety of cases. As an example, consider theAsyncResult following simple interface to transfer files from client to server: Slice interface FileTransfer { void send(int offset, ByteSeq bytes); }; The client repeatedly calls to send a chunk of the file, indicating at which offset in the file the chunk belongs. A naïve way to transmit asend file would be along the following lines: C# FileHandle file = open(...); FileTransferPrx ft = ...; const int chunkSize = ...; int offset = 0; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Read a chunk ft.send(offset, bs); // Send the chunk offset += bs.Length; } This works, but not very well: because the client makes synchronous calls, it writes each chunk on the wire and then waits for the server to receive the data, process it, and return a reply before writing the next chunk. This means that both client and server spend much of their time doing nothing — the client does nothing while the server processes the data, and the server does nothing while it waits for the client to send the next chunk. Using asynchronous calls, we can improve on this considerably:Ice 3.4.2 Documentation 463 Copyright © 2011, ZeroC, Inc. C# FileHandle file = open(...); FileTransferPrx ft = ...; const int chunkSize = ...; int offset = 0; LinkedList results = new LinkedList(); const int numRequests = 5; while (!file.eof()) { byte[] bs; bs = file.read(chunkSize); // Send up to numRequests + 1 chunks asynchronously. Ice.AsyncResult r = ft.begin_send(offset, bs); offset += bs.Length; // Wait until this request has been passed to the transport. r.waitForSent(); results.AddLast(r); // Once there are more than numRequests, wait for the least // recent one to complete. while (results.Count > numRequests) { Ice.AsyncResult r = results.First; results.RemoveFirst(); r.waitForCompleted(); } } // Wait for any remaining requests to complete. while (results.Count > 0) { Ice.AsyncResult r = results.First; results.RemoveFirst(); r.waitForCompleted(); } With this code, the client sends up to chunks before it waits for the least recent one of these requests to complete. InnumRequests + 1 other words, the client sends the next request without waiting for the preceding request to complete, up to the limit set by . InnumRequests effect, this allows the client to "keep the pipe to the server full of data": the client keeps sending data, so both client and server continuously do work. Obviously, the correct chunk size and value of depend on the bandwidth of the network as well as the amount of time takennumRequests by the server to process each request. However, with a little testing, you can quickly zoom in on the point where making the requests larger or queuing more requests no longer improves performance. With this technique, you can realize the full bandwidth of the link to within a percent or two of the theoretical bandwidth limit of a native socket connection. Generic Completion Callbacks in C# The method is overloaded to allow you to provide completion callbacks. Here are the corresponding methods for the begin_ getName operation:Ice 3.4.2 Documentation 464 Copyright © 2011, ZeroC, Inc. C# Ice.AsyncResult begin_getName( int number, Ice.AsyncCallback cb__, object cookie__); Ice.AsyncResult begin_getName( int number, _System.Collections.Generic.Dictionary ctx__, Ice.AsyncCallback cb__, object cookie__); The second version of lets you override the default context. (We discuss the purpose of the parameter in the nextbegin_getName cookie section.) Following the in-parameters, the method accepts a parameter of type , which is a delegate for abegin_ Ice.AsyncCallback callback method. The Ice run time invokes the callback method when an asynchronous operation completes. Your callback method must have return type and accept a single parameter of type , for example:void AsyncResult C# private class MyCallback { public void finished(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); try { string name = e.end_getName(r); System.Console.WriteLine("Name is: " + name); } catch (Ice.Exception ex) { System.Console.Err.WriteLine("Exception is: " + ex); } } } The implementation of your callback method must call the method. The proxy for the call is available via the method on the end_ getProxy that is passed by the Ice run time. The return type of is , so you must down-cast the proxy to itsAsyncResult getProxy Ice.ObjectPrx correct type. Your callback method should catch and handle any exceptions that may be thrown by the method. If you allow an exception to escapeend_ from the callback method, the Ice run time produces a log entry by default and ignores the exception. (You can disable the log message by setting the property to zero.)Ice.Warn.AMICallback To inform the Ice run time that you want to receive a callback for the completion of the asynchronous call, you pass a delegate for your callback method to the method:begin_ C# EmployeesPrx e = ...; MyCallback cb = new MyCallback(); Ice.AsyncCallback del = new Ice.AsyncCallback(cb.finished); e.begin_getName(99, del, null); The trailing argument specifies a cookie, which we will discuss shortly.null You can avoid explicit instantiation of the delegate and, more tersely, write:Ice 3.4.2 Documentation 465 Copyright © 2011, ZeroC, Inc. C# EmployeesPrx e = ...; MyCallback cb = new MyCallback(); e.begin_getName(99, cb.finished, null); Using Cookies for Generic Completion Callbacks in C# It is common for the method to require access to some state that is established by the code that calls the method. As anend_ begin_ example, consider an application that asynchronously starts a number of operations and, as each operation completes, needs to update different user interface elements with the results. In this case, the method knows which user interface element should receive thebegin_ update, and the method needs access to that element.end_ The API allows you to pass such state by providing a cookie. A cookie is any class instance; the class can contain whatever data you want to pass, as well as any methods you may want to add to manipulate that data. Here is an example implementation that stores a . (We assume that this class provides whatever methods are needed by the Widget end_ method to update the display.) When you call the method, you pass the appropriate cookie instance to inform the method howbegin_ end_ to update the display: C# // Invoke the getName operation with different widget cookies. MyCallback cb = ...; e.begin_getName(99, cb.finished, widget1); e.begin_getName(24, cb.finished, widget2); The method can retrieve the cookie from the by reading the property. For this example, we assume thatend_ AsyncResult AsyncState widgets have a method that updates the relevant UI element:writeString C# public void finished(Ice.AsyncResult r) { EmployeesPrx e = (EmployeesPrx)r.getProxy(); Widget widget = (Widget)r.AsyncState; try { string name = e.end_getName(r); widget.writeString(name); } catch (Ice.Exception ex) { handleException(ex); } } The cookie provides a simple and effective way for you to pass state between the point where an operation is invoked and the point where its results are processed. Moreover, if you have a number of operations that share common state, you can pass the same cookie instance to multiple invocations. Type-Safe Completion Callbacks in C# The is not entirely type-safe:generic callback API You must down-cast the return value of to the correct proxy type before you can call the method.getProxy end_ You must call the correct method to match the operation called by the method.end_ begin_ You must remember to catch exceptions when you call the method; if you forget to do this, you will not know that the operationend_ failed. slice2cs generates an additional type-safe API that takes care of these chores for you. To use type-safe callbacks, you supply delegates for two callback methods:Ice 3.4.2 Documentation 466 Copyright © 2011, ZeroC, Inc. a success callback that is called if the operation succeeds a failure callback that is called if the operation raises an exception Here is a callback class for an invocation of the operation:getName C# public class MyCallback { public void getNameCB(string name) { System.Console.WriteLine("Name is: " + name); } public void failureCB(Ice.Exception ex) { System.Console.Err.WriteLine("Exception is: " + ex); } } The callback methods can have any name you prefer and must have return type. The failure callback always has a single parameter ofvoid type . The success callback parameters depend on the operation signature. If the operation has non- return type, theIce.Exception void first parameter of the success callback is the return value. The return value (if any) is followed by a parameter for each out-parameter of the corresponding Slice operation, in the order of declaration. At the calling end, you call the method as follows:begin_ C# MyCallback cb = new MyCallback(); e.begin_getName(99).whenCompleted(cb.getNameCB, cb.failureCB); Note the method on the that is returned by the method. This method establishes the link betweenwhenCompleted AsyncResult begin_ the method and the callbacks that are called by the Ice run time by setting the delegates for the success and failure methods.begin_ It is legal to pass a null delegate for the success or failure methods. For the success callback, this is legal only for operations that have void return type and no out-parameters. This is useful if you do not care when the operation completes but want to know if the call failed. If you pass a null exception delegate, the Ice run time will ignore any exception that is raised by the invocation. Using Cookies for Type-Safe Completion Callbacks in C# The type-safe API does not support cookies. If you want to pass state from the method to the method, you must use the begin_ end_ or, alternatively, place the state into the callback class containing the callback methods. Here is a simple implementation of ageneric API callback class that stores a widget that can be retrieved by the method:end_Ice 3.4.2 Documentation 467 Copyright © 2011, ZeroC, Inc. C# public class MyCallback { public MyCallback(Widget w) { _w = w; } private Widget _w; public void getNameCB(string name) { _w.writeString(name); } public void failureCB(Ice.Exception ex) { _w.writeError(ex); } } When you call the method, you pass the appropriate callback instance to inform the method how to update the display:begin_ end_ C# EmployeesPrx e = ...; Widget widget1 = ...; Widget widget2 = ...; MyCallback cb1 = new MyCallback(widget1); MyCallback cb2 = new MyCallback(widget2); // Invoke the getName operation with different widget callbacks. e.begin_getName(99).whenCompleted(cb1.getNameCB, cb1.failureCB); e.begin_getName(24).whenCompleted(cb2.getNameCB, cb2.failureCB); Asynchronous Oneway Invocations in C# You can invoke operations via oneway proxies asynchronously, provided the operation has return type, does not have anyvoid out-parameters, and does not raise user exceptions. If you call the method on a oneway proxy for an operation that returns valuesbegin_ or raises a user exception, the method throws a .begin_ System.ArgumentException For the generic API, the callback method looks exactly as for a twoway invocation. However, for oneway invocations, the Ice run time does not call the callback method unless the invocation raised an exception during the method ("on the way out").begin_ For the type-safe API, you only specify a delegate for the failure method. For example, here is how you could call ice_ping asynchronously: C# ObjectPrx p = ...; MyCallback cb = new MyCallback(); p.begin_ice_ping().whenCompleted(cb.failureCB); Flow Control in C# Asynchronous method invocations never block the thread that calls the method: the Ice run time checks to see whether it can writebegin_Ice 3.4.2 Documentation 468 Copyright © 2011, ZeroC, Inc. the request to the local transport. If it can, it does so immediately in the caller's thread. (In that case, AsyncResult.sentSynchronously returns true.) Alternatively, if the local transport does not have sufficient buffer space to accept the request, the Ice run time queues the request internally for later transmission in the background. (In that case, returns false.)AsyncResult.sentSynchronously This creates a potential problem: if a client sends many asynchronous requests at the time the server is too busy to keep up with them, the requests pile up in the client-side run time until, eventually, the client runs out of memory. The API provides a way for you to implement flow control by counting the number of requests that are queued so, if that number exceeds some threshold, the client stops invoking more operations until some of the queued operations have drained out of the local transport. For the , you can create an additional callback method:generic API C# public class MyCallback { public void finished(Ice.AsyncResult r) { // ... } public void sent(Ice.AsyncResult r) { // ... } } As with any other callback method, you are free to choose any name you like. For this example, the name of the callback method is .sent You inform the Ice run time that you want to be informed when a call has been passed to the local transport by calling :whenSent C# MyCallback cb = new MyCallback(); e.begin_getName(99).whenCompleted(cb.getNameCB, cb.failureCB).whenSent(cb.sent); If the Ice run time can immediately pass the request to the local transport, it does so and invokes the method from the thread that callssent the method. On the other hand, if the run time has to queue the request, it calls the method from a different thread once it hasbegin_ sent written the request to the local transport. In addition, you can find out from the that is returned by the methodAsyncResult begin_ whether the request was sent synchronously or was queued, by calling .sentSynchronously For the , the method has the following signature:generic API sent C# void sent(Ice.AsyncResult r); For the , the signature is:type-safe API C# void sent(bool sentSynchronously); For the generic API, you can find out whether the request was sent synchronously by calling on the .sentSynchronously AsyncResult For the type-safe API, the boolean parameter provides the same information.sentSynchronously The methods allow you to limit the number of queued requests by counting the number of requests that are queued and decrementingsent the count when the Ice run time passes a request to the local transport. Asynchronous Batch Requests in C# Applications that send can either flush a batch explicitly or allow the Ice run time to flush automatically. The proxy method batched requestsIce 3.4.2 Documentation 469 Copyright © 2011, ZeroC, Inc. performs an immediate flush using the synchronous invocation model and may block the calling thread untilice_flushBatchRequests the entire message can be sent. Ice also provides asynchronous versions of this method so you can flush batch requests asynchronously. begin_ice_flushBatchRequests and are proxy methods that flush any batch requests queued byend_ice_flushBatchRequests that proxy. In addition, similar methods are available on the communicator and the object that is returned by Connection . These methods flush batch requests sent via the same communicator and via the same connection,AsyncResult.getConnection respectively. Concurrency Semantics for AMI in C# The Ice run time always invokes your callback methods from a separate thread, with one exception: it calls the callback from thesent thread calling the method if the request could be sent synchronously. In the callback, you know which thread is calling thebegin_ sent callback by looking at the member or parameter.sentSynchronously AMI Limitations in C# AMI invocations cannot be sent using collocated optimization. If you attempt to invoke an AMI operation using a proxy that is configured to use , the Ice run time raises if the servant happens to be collocated; thecollocation optimization CollocationOptimizationException request is sent normally if the servant is not collocated. You can disable this optimization if necessary. See Also Request Contexts Batched Invocations Location TransparencyIce 3.4.2 Documentation 470 Copyright © 2011, ZeroC, Inc. slice2cs Command-Line Options The Slice-to-C# compiler, , offers the following command-line options in addition to the standard options described in slice2cs Using the :Slice Compilers --tie Generate .tie classes --impl Generate sample implementation files. This option will not overwrite an existing file. --impl-tie Generate sample implementation files using . This option will not overwrite an existing file.tie classes --checksum Generate for Slice definitions.checksums --stream Generate for Slice types.streaming helper functions See Also Using the Slice Compilers Tie Classes in C-Sharp Streaming InterfacesIce 3.4.2 Documentation 471 Copyright © 2011, ZeroC, Inc. Using Slice Checksums in C-Sharp The Slice compilers can optionally generate of Slice definitions. For , the option causes the compiler tochecksums slice2cs --checksum generate checksums in each C# source file that are added to a member of the class:Ice.SliceChecksums C# namespace Ice { public sealed class SliceChecksums { public readonly static SliceChecksumDict checksums; } } The map is initialized automatically prior to first use; no action is required by the application.checksums In order to verify a server's checksums, a client could simply compare the dictionaries using the function. However, this is notEquals feasible if it is possible that the server might be linked with more Slice definitions than the client. A more general solution is to iterate over the local checksums as demonstrated below: C# Ice.SliceChecksumDict serverChecksums = ... foreach(System.Collections.DictionaryEntry e in Ice.SliceChecksums.checksums) { string checksum = serverChecksums[e.Key]; if (checksum == null) { // No match found for type id! } else if (!checksum.Equals(e.Value)) { // Checksum mismatch! } } In this example, the client first verifies that the server's dictionary contains an entry for each Slice type ID, and then it proceeds to compare the checksums. See Also Slice ChecksumsIce 3.4.2 Documentation 472 Copyright © 2011, ZeroC, Inc. Example of a File System Client in C-Sharp This page presents a very simple client to access a server that implements the file system we developed in .Slice for a Simple File System The C# code hardly differs from the code you would write for an ordinary C# program. This is one of the biggest advantages of using Ice: accessing a remote object is as easy as accessing an ordinary, local C# object. This allows you to put your effort where you should, namely, into developing your application logic instead of having to struggle with arcane networking APIs. This is true for the as well,server side meaning that you can develop distributed applications easily and efficiently. We now have seen enough of the client-side C# mapping to develop a complete client to access our remote file system. For reference, here is the Slice definition once more: Slice module Filesystem { interface Node { idempotent string name(); }; exception GenericError { string reason; }; sequence Lines; interface File extends Node { idempotent Lines read(); idempotent void write(Lines text) throws GenericError; }; sequence NodeSeq; interface Directory extends Node { idempotent NodeSeq list(); }; }; To exercise the file system, the client does a recursive listing of the file system, starting at the root directory. For each node in the file system, the client shows the name of the node and whether that node is a file or directory. If the node is a file, the client retrieves the contents of the file and prints them. The body of the client code looks as follows: C# using System; using Filesystem; public class Client { // Recursively print the contents of directory "dir" // in tree fashion. For files, show the contents of // each file. The "depth" parameter is the current // nesting level (for indentation). static void listRecursive(DirectoryPrx dir, int depth) { string indent = new string('\t', ++depth); NodePrx[] contents = dir.list(); foreach (NodePrx node in contents) DirectoryPrx subdir = DirectoryPrxHelper.checkedCast(node); FilePrx file = FilePrxHelper.uncheckedCast(node);Ice 3.4.2 Documentation 473 Copyright © 2011, ZeroC, Inc. Console.WriteLine( indent + node.name() + (subdir != null ? " (directory):" : " (file):")); if (subdir != null) { listRecursive(subdir, depth); } else { string[] text = file.read(); for (int j = 0; j < text.Length; ++j) Console.WriteLine(indent + "\t" + text[j]); } } } public static void Main(string[] args) { int status = 0; Ice.Communicator ic = null; try { // Create a communicator // ic = Ice.Util.initialize(ref args); // Create a proxy for the root directory // Ice.ObjectPrx obj = ic.stringToProxy("RootDir:default -p 10000"); // Down-cast the proxy to a Directory proxy // DirectoryPrx rootDir = DirectoryPrxHelper.checkedCast(obj); // Recursively list the contents of the root directory // Console.WriteLine("Contents of root directory:"); listRecursive(rootDir, 0); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } if (ic != null) { // Clean up // try { ic.destroy(); } catch (Exception e) { Console.Error.WriteLine(e); status = 1; } } Environment.Exit(status);Ice 3.4.2 Documentation 474 Copyright © 2011, ZeroC, Inc. 1. 2. 1. 2. 3. } } The class defines two methods: , which is a helper function to print the contents of the file system, and ,Client listRecursive Main which is the main program. Let us look at first:Main The structure of the code in follows what we saw in . After initializing the run time, the client creates aMain Hello World Application proxy to the root directory of the file system. For this example, we assume that the server runs on the local host and listens using the default protocol (TCP/IP) at port 10000. The object identity of the root directory is known to be .RootDir The client down-casts the proxy to and passes that proxy to , which prints the contents of the fileDirectoryPrx listRecursive system. Most of the work happens in . The function is passed a proxy to a directory to list, and an indent level. (The indent levellistRecursive increments with each recursive call and allows the code to print the name of each node at an indent level that corresponds to the depth of the tree at that node.) calls the operation on the directory and iterates over the returned sequence of nodes:listRecursive list The code does a to narrow the proxy to a proxy, as well as an to narrow the checkedCast Node Directory uncheckedCast proxy to a proxy. Exactly one of those casts will succeed, so there is no need to call twice: if the Node File checkedCast Node , the code uses the returned by the ; if the fails, we that theis-a Directory DirectoryPrx checkedCast checkedCast know Node File and, therefore, an is sufficient to get a . is-a uncheckedCast FilePrx In general, if you know that a down-cast to a specific type will succeed, it is preferable to use an instead of a uncheckedCast because an does not incur any network traffic.checkedCast uncheckedCast The code prints the name of the file or directory and then, depending on which cast succeeded, prints or "(directory)" following the name."(file)" The code checks the type of the node: If it is a directory, the code recurses, incrementing the indent level. If it is a file, the code calls the operation on the file to retrieve the file contents and then iterates over the returnedread sequence of lines, printing each line. Assume that we have a small file system consisting of two files and a directory as follows: A small file system. The output produced by the client for this file system is: Contents of root directory: README (file): This file system contains a collection of poetry. Coleridge (directory): Kubla_Khan (file): In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea.Ice 3.4.2 Documentation 475 Copyright © 2011, ZeroC, Inc. Note that, so far, our client (and server) are not very sophisticated: The protocol and address information are hard-wired into the code. The client makes more remote procedure calls than strictly necessary; with minor redesign of the Slice definitions, many of these calls can be avoided. We will see how to address these shortcomings in our discussions of and .IceGrid object life cycle See Also Hello World Application Slice for a Simple File System Example of a File System Server in C-Sharp Object Life Cycle IceGridIce 3.4.2 Documentation 476 Copyright © 2011, ZeroC, Inc. Server-Side Slice-to-C-Sharp Mapping The mapping for Slice data types to C# is identical on the client side and server side. This means that everything in the Client-Side also applies to the server side. However, for the server side, there are a few additional things you need to know —Slice-to-C-Sharp Mapping specifically, how to: Initialize and finalize the server-side run time Implement servants Pass parameters and throw exceptions Create servants and register them with the Ice run time. Because the mapping for Slice data types is identical for clients and servers, the server-side mapping only adds a few additional mechanisms to the client side: a small API to initialize and finalize the run time, plus a few rules for how to derive servant classes from skeletons and how to register servants with the server-side run time. Although the examples we present in this chapter are very simple, they accurately reflect the basics of writing an Ice server. Of course, for more sophisticated servers, you will be using , for example, to improve performance or scalability. However, these APIs areadditional APIs all described in Slice, so, to use these APIs, you need not learn any C# mapping rules beyond those described here. Topics The Server-Side main Method in C-Sharp Server-Side C-Sharp Mapping for Interfaces Parameter Passing in C-Sharp Raising Exceptions in C-Sharp Tie Classes in C-Sharp Object Incarnation in C-Sharp Asynchronous Method Dispatch (AMD) in C-Sharp Example of a File System Server in C-SharpIce 3.4.2 Documentation 477 Copyright © 2011, ZeroC, Inc. The Server-Side main Method in C-Sharp On this page: A Basic Method in C#Main The Class in C#Ice.Application Using on the Client Side in C#Ice.Application Catching Signals in C# and Properties in C#Ice.Application Limitations of in C#Ice.Application A Basic Method in C#Main The main entry point to the Ice run time is represented by the local interface . As for the client side, you must initializeIce::Communicator the Ice run time by calling before you can do anything else in your server. returns aIce.Util.initialize Ice.Util.initialize reference to an instance of an :Ice.Communicator C# using System; public class Server { public static void Main(string[] args) { int status = 0; Ice.Communicator communicator = null; try { communicator = Ice.Util.initialize(ref args); // ... } catch (Exception ex) { Console.Error.WriteLine(ex); status = 1; } // ... } } Ice.Util.initialize accepts the argument vector that is passed to by the operating system. The method scans the argumentMain vector for any that are relevant to the Ice run time; any such options are removed from the argument vector so, when command-line options returns, the only options and arguments remaining are those that concern your application. If anything goes wrongIce.Util.initialize during initialization, throws an exception.initialize Before leaving your method, you call . The operation is responsible for finalizing the Ice runMain must Communicator.destroy destroy time. In particular, waits for any operation implementations that are still executing in the server to complete. In addition, destroy destroy ensures that any outstanding threads are joined with and reclaims a number of operating system resources, such as file descriptors and memory. Never allow your method to terminate without calling first; doing so has undefined behavior.Main destroy The general shape of our server-side method is therefore as follows:MainIce 3.4.2 Documentation 478 Copyright © 2011, ZeroC, Inc. C# using System; public class Server { public static void Main(string[] args) { int status = 0; Ice.Communicator communicator = null; try { communicator = Ice.Util.initialize(ref args); // ... } catch (Exception ex) { Console.Error.WriteLine(ex); status = 1; } if (communicator != null) { try { communicator.destroy(); } catch (Exception ex) { Console.Error.WriteLine(ex); status = 1; } } Environment.Exit(status); } } Note that the code places the call to into a block and takes care to return the correct exit status to theIce.Util.initialize try operating system. Also note that an attempt to destroy the communicator is made only if the initialization succeeded. The Class in C#Ice.Application The preceding structure for the method is so common that Ice offers a class, , that encapsulates all the correctMain Ice.Application initialization and finalization activities. The synopsis of the class is as follows (with some detail omitted for now): C# namespace Ice { public abstract class Application { public abstract int run(string[] args); public Application(); public Application(SignalPolicy signalPolicy); public int main(string[] args); public int main(string[] args, string configFile); public int main(string[] args, InitializationData init); public static string appName(); public static Communicator communicator(); } } The intent of this class is that you specialize and implement the abstract method in your derived class. WhateverIce.Application run code you would normally place in goes into the method instead. Using , our program looks as follows:Main run Ice.ApplicationIce 3.4.2 Documentation 479 Copyright © 2011, ZeroC, Inc. 1. 2. 3. 4. 5. 6. C# using System; public class Server { class App : Ice.Application { public override int run(string[] args) { // Server code here... return 0; } } public static void Main(string[] args) { App app = new App(); Environment.Exit(app.main(args)); } } Note that is overloaded: you can pass an optional file name or an structure.Application.main InitializationData If you pass a to , the property settings in this file are overridden by settings in a file identified by the configuration file name main environment variable (if defined). Property settings supplied on the take precedence over all other settings.ICE_CONFIG command line The method does the following:Application.main It installs an exception handler for . If your code fails to handle an exception, prints theSystem.Exception Application.main name of the exception and a stack trace on before returning with a non-zero return value.Console.Error It initializes (by calling ) and finalizes (by calling ) a communicator. You can getIce.Util.initialize Communicator.destroy access to the communicator for your server by calling the static accessor.communicator It scans the argument vector for options that are relevant to the Ice run time and removes any such options. The argument vector that is passed to your method therefore is free of Ice-related options and only contains options and arguments that are specificrun to your application. It provides the name of your application via the static method. You can get at the application name from anywhere in yourappName code by calling (which is usually required for error messages).Ice.Application.appName It installs a signal handler that properly destroys the communicator. It installs a if the application has not already configured one. The per-process logger uses the value of the per-process logger property as a prefix for its messages and sends its output to the standard error channel. An application canIce.ProgramName a