From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/orber/doc/src/ch_idl_to_erlang_mapping.xml | 1471 ++++++++++++++++++++++++ 1 file changed, 1471 insertions(+) create mode 100644 lib/orber/doc/src/ch_idl_to_erlang_mapping.xml (limited to 'lib/orber/doc/src/ch_idl_to_erlang_mapping.xml') diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml new file mode 100644 index 0000000000..0e2b049ab9 --- /dev/null +++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml @@ -0,0 +1,1471 @@ + + + + +
+ + 19972009 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + OMG IDL to Erlang Mapping + + + 1998-10-10 + + ch_idl_to_erlang_mapping.xml +
+ +
+ OMG IDL to Erlang Mapping - Overview +

The purpose of OMG IDL, Interface Definition Language, mapping + is to act as translator between platforms and languages. An IDL + specification is supposed to describe data types, object types etc.

+

CORBA is independent of the programming language used to construct + clients or implementations. In order to use the ORB, it is + necessary for programmers to know how to access ORB functionality + from their programming languages. It translates different IDL constructs + to a specific programming language. This chapter + describes the mapping of OMG IDL constructs to the Erlang programming + language.

+
+ +
+ OMG IDL Mapping Elements +

A complete language mapping will allow the programmer to have + access to all ORB functionality in a way that is convenient for + a specified programming language. +

+

All mapping must define the following elements: +

+ + All OMG IDL basic and constructed types + References to constants defined in OMG IDL + References to objects defined in OMG IDL + Invocations of operations, including passing of + parameters and receiving of results + Exceptions, including what happens when an operation + raises an exception and how the exception parameters are + accessed + Access to attributes + Signatures for operations defined by the ORB, such as + dynamic invocation interface, the object adapters etc. + Scopes; + OMG IDL has several levels of scopes, which are mapped to Erlang's + two scopes. + +
+ +
+ Getting Started +

To begin with, we should decide which type of objects (i.e. servers) we + need and if two, or more, should export the same functionality. Let us + assume that we want to create a system for DB (database) access for different + kind of users. For example, anyone with a valid password may extract + data, but only a few may update the DB. Usually, an application + is defined within a module, and all global datatypes are defined + on the top-level. To begin with we create a module and the interfaces we + need:

+ +// DB IDL +#ifndef _DB_IDL_ +#define _DB_IDL_ +// A module is simply a container +module DB { + + // An interface maps to a CORBA::Object. + interface CommonUser { + + }; + + // Inherit the Consumer interface + interface Administrator : CommonUser { + + }; + + interface Access { + + }; + +}; +#endif +

Since the Administrator should be able to do the same things as the + CommonUser, the previous inherits from the latter. The Access + interface will grant access to the DB. + Now we are ready to define the functionality and data types we need. But, this + requires that we know a little bit more about the OMG IDL.

+ +

The OMG defines a set of reserved case insensitive key-words, which may + NOT be used as identifiers (e.g. module name). For more + information, see + Reserved Compiler Names and Keywords

+
+
+ +
+ Basic OMG IDL Types +

The OMG IDL mapping is strongly typed and, even if you have a good knowledge + of CORBA types, it is essential to read carefully the following mapping to + Erlang types.

+

The mapping of basic types is straightforward. Note that the + OMG IDL double type is mapped to an Erlang float which does not + support the full double value range.

+ + + OMG IDL type + Erlang type + Note + + + float + Erlang float + + + + double + Erlang float + value range not supported + + + short + Erlang integer + -2^15 .. 2^15-1 + + + unsigned short + Erlang integer + 0 .. 2^16-1 + + + long + Erlang integer + -2^31 .. 2^31-1 + + + unsigned long + Erlang integer + 0 .. 2^32-1 + + + long long + Erlang integer + -2^63 .. 2^63-1 + + + unsigned long long + Erlang integer + 0 .. 2^64-1 + + + char + Erlang integer + ISO-8859-1 + + + wchar + Erlang integer + UTF-16 (ISO-10646-1:1993) + + + boolean + Erlang atom + true/false + + + octet + Erlang integer + + + + any + Erlang record + #any{typecode, value} + + + long double + Not supported + + + + Object + Orber object reference + Internal Representation + + + void + Erlang atom + ok + + OMG IDL basic types +
+

The any value is written as a record with the field typecode which + contains the Type Code is a full definition of a type representation, + see also the Type Code table, + and the value field itself.

+

Functions with return type void will return the atom ok.

+
+ +
+ Template OMG IDL Types and Complex Declarators +

Constructed types all have native mappings as shown in the table + below.

+ + + Type + IDL code + Maps to + Erlang code + + + string + typedef string S;

+void op(in S a);
+ Erlang string + ok = op(Obj, "Hello World"), +
+ + wstring + typedef wstring S;

+void op(in S a);
+ Erlang list of Integers + ok = op(Obj, "Hello World"), +
+ + sequence + typedef sequence <long, 3> S;

+void op(in S a);
+ Erlang list + ok = op(Obj, [1, 2, 3]), +
+ + array + typedef string S[2];

+void op(in S a);
+ Erlang tuple + ok = op(Obj, {"one", "two"}), +
+ + fixed + typedef fixed<3,2> myFixed;

+void op(in myFixed a);
+ Erlang tuple + MF = fixed:create(3, 2, 314),

+ok = op(Obj, MF),
+
+ OMG IDL Template and Complex Declarators +
+ +
+ String/WString Data Types +

A string consists of all possible 8-bit quantities except null. + Most ORB:s uses, including Orber, the character set Latin-1 (ISO-8859-1). + The wstring type is represented as a list of integers, where + each integer represents a wide character. In this case Orber uses, as + most other ORB:s, the UTF-16 (ISO-10646-1:1993) character set.

+

When defining a a string or wstring they can be of limited length or + null terminated:

+ myString10; +typedef wstring<10> myWString10; + ]]> +

If we want to define a char/string or wchar/wstring constant, we can + use octal (\\OOO - one, two or three octal digits), + hexadecimal (\\xHH - one or two hexadecimal digits) and unicode (\\uHHHH - + one, two, three or four hexadecimal digits.) representation as well. + For example:

+ +const string SwedensBestSoccerTeam = "\\101" "\\x49" "\\u004B"; +const wstring SwedensBestHockeyTeam = L"\\101\\x49\\u004B"; +const char aChar = '\\u004B'; +const wchar aWchar = L'\\u004C'; + +

Naturally, we can use "Erlang", L"Rocks", 'A' + and L'A' as well.

+
+ +
+ Sequence Data Type +

A sequence can be defined to be of a maximum length or unbounded, and may + contain Basic and Template types and scoped names:

+ aShortSequence; +typedef sequence aLongSequence; +typedef sequence anEvenLongerSequence; + ]]> +
+ +
+ Array Data Type +

Arrays are multidimensional, fixed-size arrays. The indices is language + mapping specific, which is why one should not pass them as arguments + to another ORB.

+ +typedef long myMatrix[2][3]; + +
+ +
+ Fixed Data Type +

A Fixed Point literal consists of an integer part (decimal digits), + decimal point and a fraction part (decimal digits), + followed by a D or d. Either the integer part or the + fraction part may be missing; the decimal point may be missing, + but not d/D. The integer part must be a positive integer less than 32. + The Fraction part must be a positive integer less than or equal to + the Integer part.

+ +const fixed myFixed1 = 3.14D; +const fixed myFixed2 = .14D; +const fixed myFixed3 = 0.14D; +const fixed myFixed4 = 3.D; +const fixed myFixed5 = 3D; + +

It is also possible to use unary (+-) and binary (+-*/) operators:

+ +const fixed myFixed6 = 3D + 0.14D; +const fixed myFixed7 = -3.14D; + +

The Fixed Point examples above are, so called, anonymous + definitions. In later CORBA specifications these have been deprecated + as function parameters or return values. Hence, we strongly recommend that + you do not use them. Instead, you should use:

+ myFixed53; +const myFixed53 myFixed53constant = 03.140d; +typedef fixed<3,2> myFixed32; +const myFixed32 myFixed32constant = 3.14d; + +myFixed53 foo(in myFixed32 MF); // OK +void bar(in fixed<5,3> MF); // Illegal + ]]> +
+

For more information, see Fixed in + Orber's Reference Manual.

+

Now we continue to work on our IDL specification. To begin with, we want + to limit the size of the logon parameters (Id and password). Since the + UserID and Password parameters, only will be used when + invoking operations on the Access interface, we may choose to define + them within the scope that interface. To keep it simple our DB will contain + employee information. Hence, as the DB key we choose an integer + (EmployeeNo).

+ UserID; + typedef string<10> Password; + + CommonUser logon(in UserID ID, in Password PW); + + }; + +}; +#endif ]]> +

But what should, for example, the lookup operation return? One option + is to use the any data type. But, depending on what kind of data it + encapsulates, this datatype can be rather expensive to use. We might find a + solution to our problems among the Constructed IDL types.

+
+ +
+ Constructed OMG IDL Types +

Constructed types all have native mappings as shown in the table + below.

+ + + Type + IDL code + Maps to + Erlang code + + + struct + struct myStruct {

+long a;

+short b;

+};

+void op(in myStruct a);
+ Erlang record + ok = op(Obj, #'myStruct'{a=300, b=127}), +
+ + union + union myUnion switch(long) {

+case 1: long a;

+};

+void op(in myUnion a);
+ Erlang record + ok = op(Obj, #'myUnion'{label=1, value=66}), +
+ + enum + enum myEnum {one, two};

+void op(in myEnum a);
+ Erlang atom + ok = op(Obj, one), +
+ OMG IDL constructed types +
+ +
+ Struct Data Type +

A struct may have Basic, Template, Scoped Names and Constructed + types as members.

+
+ +
+ Enum Data Type +

The maximum number of identifiers which may defined in an enumeration + is 2³². The order in which the identifiers are named in the + specification of an enumeration defines the relative order of the + identifiers.

+
+ +
+ Union Data Type +

A union may consist of:

+ + Identifier + Switch - may be an integer, char, boolean, enum or scoped name. + Body - with or without a default case; may appear at + most once. + +

A case label must match the defined type of the discriminator, and may only + contain a default case if the values given in the non-default labels do + not cover the entire range of the union's discriminant type. For example:

+ +// Illegal default; all cases covered by +// non-default cases. +union BooleanUnion switch(boolean) { + case TRUE: long TrueValue; + case FALSE: long FalseValue; + default: long DefaultValue; +}; +// OK +union BooleanUnion2 switch(boolean) { + case TRUE: long TrueValue; + default: long DefaultValue; +}; + +

It is not necessary to list all possible values of the union discriminator + in the body. Hence, the value of a union is the value of the discriminator + and, in given order, one of the following:

+ + If the discriminator match a label, explicitly listed in a + case statement, the value must be of the same type. + If the union contains a default label, the value must match the + type of the default label. + No value. Orber then inserts the Erlang atom undefined + in the value field when receiving a union from an external + ORB. + +

The above can be summed up to:

+ +// If the discriminator equals 1 or 2 the value +// is a long. Otherwise, the atom undefined. +union LongUnion switch(long) { + case 1: + case 2: long TrueValue; +}; +// If the discriminator equals 1 or 2 the value +// is a long. Otherwise, a boolean. +union LongUnion2 switch(long) { + case 1: + case 2: long TrueValue; + default: boolean DefaultValue; +}; + +
+ +

Every field in, for example, a struct must be initiated. Otherwise + it will be set to the atom undefined, which Orber cannot + encode when communicating via IIOP. In the example above, invoking + the operation with #'myStruct'{a=300} will fail (equal to + #'myStruct'{a=300, b=undefined})

+
+

Now we can continue to work on our IDL specification. To begin with, we should + determine the return value of the lookup operation. Since the any + type can be rather expensive we can use a struct or a union instead. + If we intend to return the same information about a employee every time we can + use a struct. Let us assume that the DB contains the name, address, employee + number and department.

+ UserID; + typedef string<10> Password; + + // Since Administrator inherits from CommonUser + // the returned Object can be of either type. + CommonUser logon(in UserID ID, in Password PW); + + }; + +}; +#endif ]]> +

We can also define exceptions (i.e. not system exception) thrown by + each interface. Since exceptions are thoroughly described in the chapter + System and User Defined Exceptions, + we choose not to. Hence, we are now ready to compile our IDL-file by + invoking:

+
+$ erlc DB.idl
+    
+

or:

+
+$ erl
+Erlang (BEAM) emulator version 5.1.1 [threads:0]
+
+Eshell V5.1.1  (abort with ^G)
+1> ic:gen('DB').
+ok
+2> halt().
+    
+

The next step is to implement our servers. But, to be able to do that, + we need to know how we can access data type definitions. For example, + since a struct is mapped to an Erlang record we must include an hrl-file + in our callback module.

+
+ +
+ Scoped Names and Generated Files + +
+ Scoped Names +

Within a scope all identifiers must be unique. The following kinds of + definitions form scopes in the OMG IDL:

+ + module + interface + operation + valuetype + struct + union + exception + +

For example, since enumerants do not form a scope, the following IDL code + is not valid:

+ +module MyModule { + // 'two' is not unique + enum MyEnum {one, two}; + enum MyOtherEnum {two, three}; +}; + +

But, since Erlang only has two levels of scope, module and + function, the OMG IDL scope is mapped as follows:

+ + Function Scope - used for constants, operations and attributes. + Erlang Module Scope - the Erlang module scope + handles the remaining OMG IDL scopes. + +

An Erlang module, corresponding to an IDL global name, is derived by + converting occurrences of "::" to underscore, and eliminating + the leading "::". Hence, accessing MyEnum from another module, one + use MyModule::MyEnum

+

For example, an operation foo defined in interface I, which + is defined in module M, would be written in IDL as M::I::foo + and as 'M_I':foo in Erlang - foo is the function + name and 'M_I' is the name of the Erlang module. Applying this + knowledge to a stripped version of the DB.idl gives:

+ UserID; + typedef string<10> Password; + + // Since Administrator inherits from CommonUser + // the returned Object can be of either type. + // This operation is exported from: + // DB_Access.erl + CommonUser logon(in UserID ID, in Password PW); + + }; + +}; +#endif ]]> +

Using underscores in IDL names can lead to ambiguities + due to the name mapping described above. It is advisable to + avoid the use of underscores in identifiers. For example, the following + definition would generate two structures named x_y_z.

+ +module x { + + struct y_z { +\011... + }; + + interface y { + +\011struct z { +\011 ... +\011}; + }; +}; + +
+ +
+ Generated Files +

Several files can be generated for each scope.

+ + An Erlang source code file (.erl) is generated + for top level scope as well as the Erlang header file. + An Erlang header file (.hrl) will be generated for + each scope. The header file will contain record definitions + for all struct, union and exception + types in that scope. + Modules that contain at least one constant definition, + will produce Erlang source code files (.erl). + That Erlang file will contain constant functions for + that scope. + Modules that contain no constant definitions are considered + empty and no code will be produced for them, but only for + their included modules/interfaces. + Interfaces will produce Erlang source code files (.erl), + this code will contain all operation stub code and implementation + functions. + In addition to the scope-related files, an Erlang source file will + be generated for each definition of the types struct, + union and exception (these are the types that + will be represented in Erlang as records). + This file will contain special access functions for that record. + The top level scope will produce two files, one header file + (.hrl) and one Erlang source file (.erl). + These files are named as the IDL file, prefixed with oe_. + +

After compiling DB.idl, the following files have been generated:

+ + oe_DB.hrl and oe_DB.erl for the top scope level. + DB.hrl for the module DB. + DB_Access.hrl and DB_Access.erl for the interface + DB_Access. + DB_CommonUser.hrl and DB_CommonUser.erl for the interface + DB_CommonUser. + DB_Administrator.hrl and DB_Administrator.erl for the interface + DB_Administrator. + DB_employee.erl for the structure employee in module + DB. + +

Since the employee struct is defined in the top level scope, + the Erlang record definition is found in DB.hrl. IC also generates + stubs/skeletons (e.g. DB_CommonUser.erl) and access functions for + some datatypes (e.g. DB_employee.erl). How the stubs/skeletons are + used is thoroughly described in + Stubs/Skeletons and + Module_Interface.

+
+
+ +
+ Typecode, Identity and Name Access Functions +

As mentioned in a previous section, struct, union and + exception types yield record definitions and access code + for that record. + For struct, union, exception, array and + sequence types, a special file is generated that holds access + functions for TypeCode, Identity and Name. + These functions are put in the file corresponding to the scope where + they are defined. For example, the module DB_employee.erl, + representing the employee struct, exports the following functions:

+ + tc/0 - returns the type code for the struct. + id/0 - returns the IFR identity of the struct. In this case + the returned value is "IDL:DB/employee:1.0", but + if the struct was defined in the scope of CommonUser, + the result would be "IDL:DB/CommonUser/employee:1.0". + However, the user usually do not need to know the Id, just + which Erlang module contains the correct Id. + name/0 - returns the scoped name of the struct. The employee + struct name is "DB_employee". + +

Type codes give a complete description of the type including all its components and structure.are, for example, used in Any values. + Hence, we can encapsulate the employee struct in an any + type by:

+ +%% Erlang code +.... +AnEmployee = #'DB_employee'{'No' = 1, + 'Name' = "Adam Ivan Kendall", + 'Address' = "Rasunda, Solna", + 'Dpt' = 'Department1'}, +EmployeeTC = 'DB_employee':tc(), +EmployeeAny = any:create(EmployeeTC, AnEmployee), +.... + +

For more information, see the + Type Code listing.

+
+ +
+ References to Constants +

Constants are generated as Erlang functions, and are accessed by a + single function call. The functions are put in the file + corresponding to the scope where they are defined. There is no + need for an object to be started to access a constant.

+

Example:

+ +// m.idl +module m { + const float pi = 3.14; + + interface i { +\011const float pi = 3.1415; + }; +}; + +

Since the two constants are defined in different scopes, the IDL code + above is valid, but not necessarily a good approach. After compiling + m.idl, the constant definitions can be extracted by invoking:

+
+$ erlc m.idl
+$ erlc m.erl
+$ erl
+Erlang (BEAM) emulator version 5.1.1 [threads:0]
+
+Eshell V5.1.1  (abort with ^G)
+1> m:pi().
+3.14
+2> m_i:pi().
+3.1415
+3> halt().
+    
+
+ +
+ References to Objects Defined in OMG IDL +

Objects are accessed by object references. An object reference + is an opaque Erlang term created and maintained by the ORB.

+

Objects are implemented by providing implementations for all + operations and attributes of the Object, see operation implementation.

+
+ +
+ Exceptions +

Exceptions are handled as Erlang catch and throws. Exceptions + are translated to messages over an IIOP bridge but converted + back to a throw on the receiving side. Object implementations + that invoke operations on other objects must be aware of the + possibility of a non-local return. This includes invocation of + ORB and IFR services. See also the + Exceptions section.

+

Exception parameters are mapped as an Erlang record and accessed + as such.

+

An object implementation that raises an exception will use the + corba:raise/1 function, passing the exception record as + parameter.

+
+ +
+ Access to Attributes +

Attributes are accessed through their access functions. An + attribute implicitly defines the _get and _set + operations. These operations are handled in the same way as + normal operations. The _get operation is defined as a readonly + attribute.

+ +readonly attribute long RAttribute; +attribute long RWAttribute; + +

The RAttribute requires that you implement, in your call-back module, + _get_RAttribute. For the RWAttribute it is necessary to implement + _get_RWAttribute and _set_RWAttribute.

+
+ +
+ Invocations of Operations + +

A standard Erlang gen_server behavior is used for + object implementation. The gen_server state is then + used as the object internal state. Implementation of the object + function is achieved by implementing its methods and attribute operations. + These functions will usually have the internal state as their first parameter, + followed by any in and inout parameters.

+

Do not confuse the + object internal state with its object reference. The object internal state is + an Erlang term which has a format defined by the user.

+ +

It is is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).

+
+

A function call will invoke an operation. The first + parameter of the function should be the object reference and then + all in and inout parameters follow in the same + order as specified in the IDL specification. The result will be a return value + unless the function has inout or out parameters specified; + in which case, a tuple of the return value, followed by the parameters will + be returned.

+

Example:

+ +// IDL +module m { + interface i { + readonly attribute long RAttribute; + attribute long RWAttribute; + long foo(in short a); + long bar(in char c, inout string s, out long count); + void baz(out long Id); + }; +}; + +

Is used in Erlang as :

+ +%% Erlang code +.... +Obj = ... %% get object reference +RAttr = m_i:'_get_RAttribute'(Obj), +RWAttr = m_i:'_get_RWAttribute'(Obj), +ok = m_i:'_set_RWAttribute'(Obj, Long), +R1 = m_i:foo(Obj, 55), +{R2, S, Count} = m_i:bar(Obj, $a, "hello"), +.... + +

Note how the inout parameter is passed and + returned. There is no way to use a single occurrence of a + variable for this in Erlang. Also note, that ok, Orber's + representation of the IDL-type void, must be returned by + baz and '_set_RWAttribute'. + These operations can be implemented in the call-back module as:

+ +'_set_RWAttribute'(State, Long) -> + {reply, ok, State}. + +'_get_RWAttribute'(State) -> + {reply, Long, State}. + +'_get_RAttribute'(State) -> + {reply, Long, State}. + +foo(State, AShort) -> + {reply, ALong, State}. + +bar(State, AShort, AString) -> + {reply, {ALong, "MyString", ALong}, State}. + +baz(State) -> + {reply, {ok, AId}, State}. + +

The operations may require more arguments (depends on IC options used). For + more information, see Stubs/Skeletons + and Module_Interface.

+ +

A function can also be defined to be oneway, i.e. + asynchronous. But, since the behavior of a oneway operation is not + defined in the OMG specifications (i.e. the behavior can differ depending on + which other ORB Orber is communicating with), one should avoid using it.

+
+
+ +
+ Implementing the DB Application +

Now we are ready to implement the call-back modules. There are three modules + we must create:

+ + DB_Access_impl.erl + DB_CommonUser_impl.erl + DB_Administrator_impl.erl + +

An easy way to accomplish that, is to use the IC backend erl_template, + which will generate a complete call-back module. One should also add + the same compile options, for example this or from, + used when generating the stub/skeleton modules:

+ +$> erlc +"{be,erl_template}" DB.idl + +

We begin with implementing the DB_Access_impl.erl module, which, + if we used erl_template, will look like the following. All we need + to do is to add the logic to the logon operation.

+ +%% +%% $Id$ +%% +%%---------------------------------------------------------------------- +%% Module : DB_Access_impl.erl +%% +%% Source : /home/user/example/DB.idl +%% +%% Description : +%% +%% Creation date: 2005-05-20 +%% +%%---------------------------------------------------------------------- +-module('DB_Access_impl'). + +-export([logon/3]). + +%%---------------------------------------------------------------------- +%% Internal Exports +%%---------------------------------------------------------------------- +-export([init/1, + terminate/2, + code_change/3, + handle_info/2]). + +%%---------------------------------------------------------------------- +%% Include Files +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Macros +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- +-record(state, {}). + +%%====================================================================== +%% API Functions +%%====================================================================== +%%---------------------------------------------------------------------- +%% Function : logon/3 +%% Arguments : State - term() +%% ID = String() +%% PW = String() +%% Returns : ReturnValue = OE_Reply +%% OE_Reply = Object_Ref() +%% Raises : +%% Description: +%%---------------------------------------------------------------------- +logon(State, ID, PW) -> +\011%% Check if the ID/PW is valid and what +\011%% type of user it is (Common or Administrator). +\011OE_Reply + = case check_user(ID, PW) of +\011 {ok, administrator} -> +\011 'DB_Administrator':oe_create(); +\011 {ok, common} -> +\011 'DB_CommonUser':oe_create(); +\011 error -> +\011 %% Here we should throw an exception + \011 corba:raise(....) + end, +\011{reply, OE_Reply, State}. + +%%====================================================================== +%% Internal Functions +%%====================================================================== +%%---------------------------------------------------------------------- +%% Function : init/1 +%% Arguments : Env = term() +%% Returns : {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Raises : - +%% Description: Initiates the server +%%---------------------------------------------------------------------- +init(_Env) -> +\011{ok, #state{}}. + + +%%---------------------------------------------------------------------- +%% Function : terminate/2 +%% Arguments : Reason = normal | shutdown | term() +%% State = term() +%% Returns : ok +%% Raises : - +%% Description: Invoked when the object is terminating. +%%---------------------------------------------------------------------- +terminate(_Reason, _State) -> +\011ok. + + +%%---------------------------------------------------------------------- +%% Function : code_change/3 +%% Arguments : OldVsn = undefined | term() +%% State = NewState = term() +%% Extra = term() +%% Returns : {ok, NewState} +%% Raises : - +%% Description: Invoked when the object should update its internal state +%% due to code replacement. +%%---------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> +\011{ok, State}. + + +%%---------------------------------------------------------------------- +%% Function : handle_info/2 +%% Arguments : Info = normal | shutdown | term() +%% State = NewState = term() +%% Returns : {noreply, NewState} | +%% {noreply, NewState, Timeout} | +%% {stop, Reason, NewState} +%% Raises : - +%% Description: Invoked when, for example, the server traps exits. +%%---------------------------------------------------------------------- +handle_info(_Info, State) -> +\011{noreply, State}. + ]]> +

Since DB_Administrator inherits from DB_CommonUser, + we must implement delete in the DB_Administrator_impl.erl + module, and lookup in DB_Administrator_impl.erlandDB_CommonUser_impl.erl. But wait, is that really necessary? Actually, + it is not. We simple use the IC compile option impl:

+
+$ erlc +'{{impl, "DB::CommonUser"}, "DBUser_impl"}'  +'{{impl, "DB::Administrator"}, "DBUser_impl"}' DB.idl
+$ erlc *.erl
+    
+

Instead of creating, and not the least, maintaining two call-back modules, + we only have to deal with DBUser_impl.erl. If we generated the + templates, we simply rename DB_Administrator_impl.erl to + DBUser_impl.erl. See also the + Exceptions chapter. + In the following example, only the implementation of the API functions + are shown:

+ +%%====================================================================== +%% API Functions +%%====================================================================== +%%---------------------------------------------------------------------- +%% Function : delete/2 +%% Arguments : State - term() +%% ENo = unsigned_Long() +%% Returns : ReturnValue = ok +%% Raises : +%% Description: +%%---------------------------------------------------------------------- +delete(State, ENo) -> + %% How we access the DB, for example mnesia, is not shown here. + case delete_employee(No) of + ok -> + {reply, ok, State}; + error -> + %% Here we should throw an exception if + %% there is no match. + corba:raise(....) + end. + +%%---------------------------------------------------------------------- +%% Function : lookup/2 +%% Arguments : State - term() +%% ENo = unsigned_Long() +%% Returns : ReturnValue = OE_Reply +%% OE_Reply = #'DB_employee'{No,Name,Address,Dpt} +%% No = unsigned_Long() +%% Name = String() +%% Address = String() +%% Dpt = Department +%% Department = 'Department1' | 'Department2' +%% Raises : +%% Description: +%%---------------------------------------------------------------------- +lookup(State, ENo) -> + %% How we access the DB, for example mnesia, is not shown here. + case lookup_employee(ENo) of + %% We assume that we receive a 'DB_employee' struct + {ok, Employee} -> + OE_Reply = Employee, + {reply, OE_Reply, State}; + error -> + %% Here we should throw an exception if + %% there is no match. + corba:raise(....) + end. + +

After you have compiled both call-back modules, and implemented the missing + functionality (e.g. lookup_employee/1), we can test our application:

+ +%% Erlang code +.... +%% Create an Access object +Acc = 'DB_Access':oe_create(), + +%% Login is Common user and Administrator +Adm = 'DB_Access':logon(A, "admin", "pw"), +Com = 'DB_Access':logon(A, "comm", "pw"), + +%% Lookup existing employee +Employee = 'DB_Administrator':lookup(Adm, 1), +Employee = 'DB_CommonUser':lookup(Adm, 1), + +%% If we try the same using the DB_CommonUser interface +%% it result in an exit since that operation is not exported. +{'EXIT', _} = (catch 'DB_CommonUser':delete(Adm, 1)), + +%% Try to delete the employee via the CommonUser Object +{'EXCEPTION', _} = (catch 'DB_Administrator':delete(Com, 1)), + +%% Invoke delete operation on the Administrator object +ok = 'DB_Administrator':delete(Adm, 1), +.... + +
+ +
+ Reserved Compiler Names and Keywords + +

The use of some names is strongly discouraged due to + ambiguities. However, the use of some names is prohibited + when using the Erlang mapping , as they are strictly reserved for IC.

+

IC reserves all identifiers starting with OE_ and oe_ + for internal use.

+

Note also, that an identifier in IDL can contain alphabetic, + digits and underscore characters, but the first character + must be alphabetic. +

+

The OMG defines a set of reserved words, shown below, for use as keywords. + These may not be used as, for example, identifiers. The keywords + which are not in bold face was introduced in the OMG CORBA-3.0 + specification.

+ + + abstract + exception + inout + provides + truncatable + + + any + emits + interface + public + typedef + + + attribute + enum + local + publishes + typeid + + + boolean + eventtype + long + raises + typeprefix + + + case + factory + module + readonly + unsigned + + + char + FALSE + multiple + setraises + union + + + component + finder + native + sequence + uses + + + const + fixed + Object + short + ValueBase + + + consumes + float + octet + string + valuetype + + + context + getraises + oneway + struct + void + + + custom + home + out + supports + wchar + + + default + import + primarykey + switch + wstring + + + double + in + private + TRUE + + + OMG IDL keywords +
+

The keywords listed above must be written exactly as shown. Any usage + of identifiers that collide with a keyword is illegal. For example, + long is a valid keyword; Long and LONG are + illegal as keywords and identifiers. But, since the OMG must be able + to expand the IDL grammar, it is possible to use Escaped Identifiers. For example, it is not unlikely that native + have been used in IDL-specifications as identifiers. One option is to + change all occurrences to myNative. Usually, it is necessary + to change programming language code that depends upon that IDL as well. + Since Escaped Identifiers just disable type checking (i.e. if it is a reserved + word or not) and leaves everything else unchanged, it is only necessary to + update the IDL-specification. To escape an identifier, simply prefix it + with _. The following IDL-code is illegal:

+ +typedef string native; +interface i { + void foo(in native Arg); + }; +}; + +

With Escaped Identifiers the code will look like:

+ +typedef string _native; +interface i { + void foo(in _native Arg); + }; +}; + +
+ +
+ Type Code Representation + +

Type Codes are used in any values. To avoid mistakes, you should + use access functions exported by the Data Types modules + (e.g. struct, union etc) or the orber_tc + module.

+ + + Type Code + Example + + + tk_null + + + + tk_void + + + + tk_short + + + + tk_long + + + + tk_longlong + + + + tk_ushort + + + + tk_ulong + + + + tk_ulonglong + + + + tk_float + + + + tk_double + + + + tk_boolean + + + + tk_char + + + + tk_wchar + + + + tk_octet + + + + tk_any + + + + tk_TypeCode + + + + tk_Principal + + + + {tk_objref, IFRId, Name} + {tk_objref, "IDL:M1\\I1:1.0", "I1"} + + + {tk_struct, IFRId, Name, [{ElemName, ElemTC}]} + {tk_struct, "IDL:M1\\S1:1.0", "S1", [{"a", tk_long}, {"b", tk_char}]} + + + {tk_union, IFRId, Name, DiscrTC, DefaultNr, [{Label, ElemName, ElemTC}]}

+Note: DefaultNr tells which of tuples in the case list that is default, or -1 if no default
+ {tk_union, "IDL:U1:1.0", "U1", tk_long, 1, [{1, "a", tk_long}, {default, "b", tk_char}]} +
+ + {tk_enum, IFRId, Name, [ElemName]} + {tk_enum, "IDL:E1:1.0", "E1", ["a1", "a2"]} + + + {tk_string, Length} + {tk_string, 5} + + + {tk_wstring, Length} + {tk_wstring, 7} + + + {tk_fixed, Digits, Scale} + {tk_fixed, 3, 2} + + + {tk_sequence, ElemTC, Length} + {tk_sequence, tk_long, 4} + + + {tk_array, ElemTC, Length} + {tk_array, tk_char, 9} + + + {tk_alias, IFRId, Name, TC} + {tk_alias, "IDL:T1:1.0", "T1", tk_short} + + + {tk_except, IFRId, Name, [{ElemName, ElemTC}]} + {tk_except, "IDL:Exc1:1.0", "Exc1", [{"a", tk_long}, {"b", {tk_string, 0}}]} + + Type Code tuples +
+
+
+ -- cgit v1.2.3