Stanford university and David Maluf specifically disclaims any warranties,including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The software provided hereunder is on an "as is" basis, and Stanford university has no obligation to provide maintenance, support, updates, enhancements,or modifications.
Because the program is licensed free of charge, there is no warranty for the program, to the extent permitted by applicable law. We provide the program "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk of using this software is yours.
Downloading the current OSF1 version:
ccnp-osf1.tar.gz
CORBA has become one of the most commonly used object standard in the industry. A lot of people are using CORBA and there are a lot of CORBA servers on the Internet. Through the Interface Repository and Dynamic Invocation Interface mechanism defined by CORBA, we can make a tool, which is also a CORBA program, to talk to unregistered CORBA server in the network it can talk to, retrieve all the object interfaces registered in the server, and make connections and invoke operations on it.
The main goal of this project is to extend the capability of CLIPS by including this CORBA tool into it. In this way, a CLIPS user can get all the interfaces of objects registered in a CORBA server by running specific commands in CLIPS. Based on those definitions, CLIPS can make these object definitions available for users to create local objects. The users can invoke operations on these objects and the tool can invoke operations on the remote CORBA servers on behalf of the user.
In this way, CLIPS users can use all the objects and operations defined in other CORBA servers without knowing any of these information beforehand. Interesting applications can also be devised from this tool by using the expert system capability of CLIPS, such as generating knowledge and using rules on the information return from numerous CORBA servers connected together. Also, as suggest in [4], programs that reason with these information can also provide sophisticated interoperation services.
In the following sections of this report, I will first give overviews for CLIPS and CORBA respectively, as well as the technologies in both areas that are related to this project. After that, I will talk about the implementation details. Finally, I will conclude with the present state of the project and potential enhancements.
CLIPS is designed to facilitate the development of software to model human knowledge or experise. There are three ways to represent knowledge in CLIPS[1]:
- Procedural Knowledge,
- Declarative Functions,
- Object-oriented Model.
The generic CLIPS interface is a simple, interactive, text-oriented, command prompt interface.
There are several predefined CLIPS classes already, with OBJECT being the root of it. The pre-defined classes inheritance diagram is shown in [1].
The objects in applications will have to communicate with each other. Heterogeneity magnifies the difficulties of communication. The goal of CORBA is to smooth over these problems. It has also been called an Object Bus, a software equivalent to a computer's hardware bus.
CORBA provides a lot of functionalities and services. However, in this section, I will just talk about the three of them: Object Request Broker, which is the underlying mechanism that allows clients invoke methods on remote objects; Interface Repository, which is an on-line database of object definitions; Dynamic Invocation Interface, which allows the dynamic construction of object invocations. The Interface Repository and Dynamic Interface Invocation are actually the heart of this project.
Conceptually, invoking a method on a remote object can take the following path: the client calls the stub, which does some processing and passes the message on down to the ORB, more specifically the client ORB runtime. This communicates with server ORB runtime, which passes the message up to the server stub, which massages the message some more before passing it up to the server. The reutrn value takes the same route in reverse.
Using the information in the Interface Repositroy, it is possible for a program to encounter an object whose interfaces was not known when the program was compiled, yet, be able to determine what operations are valid on the object and make an invocation on it.
Three new functions are defined:
(get-corba-interfaces "whale")
where whale is the CORBA server name.
This function resembles the make-instance function of CLIPS. The reason a new function is needed for making instances of CORBA objects is that a new infomation is stored also as an attribute in the local objects. This information is called CorbaObjectNum, which provides the mapping between local CLIPS objects and CORBA object references on the server. This will be more cleared in the example section 4.7. Also, the full interface will be put in a string, which is also stored as an attribute. This function takes two parameters: the instance name and the class name.
An example will be:
(make-corba-instance "bank" "bank1")
where bank is the interface name and bank1 is the name you want to give to the instance that is going to be created.
An example will be:
(invoke-corba-operations
(send [bank1] get-CorbaObjectNum)
"newAccount"
(explode$ "OBJREF account account1 STRING marcus")
)
This will become more clear in the example section 4.7.
- the module in which the interface was defined, if any,
- the name of the interface,
- the interface's attributes, and their definitions,
- the interface's operations, and their full definition, including
parameter, context and exception definitions,
- the inheritance specification of the interface.
In this section, we are going to briefly describe the steps in getting information from the Interface Repository. For detail information, please refer to the Orbix Advanced Programmer Guide[5].
Repository::_bind(":IR", host, IT_X); // in C++
The structure of the Repository object is:
interface Repository: Container {
Contained lookup_id (in RepositoryID search_id);
exception invalidLoad();
void loadIDLAll();
void loadIDLFile(in string fileName)
raises (invalidLoad);
};
After that, calling the inherited function
contents(in InterfaceName restrict_type, in boolean exclude_inherited)
returns the list of Interface Repository objects contained by the object, and can be used to navigate through the hierarchy of definitions. The first parameter is used to restrict the return type. In this tool, It is set to be "InterfaceDef" because we are only interested in all the interfaces registered in the Interface Repository. The second parameter is used to determine if inherited objects are returned or not. In this case, it is set to true to return inherited objects also. One nice thing about Interface Repository is that it will return all parent objects first before it returns the objects that are inherited from the parents.
The containment hierarachy of the Interface Repository classes can be found in [5].
interface InterfaceDef: Container, Contained {
attribute sequence RepositoryId base_interfaces;
struct FullInterfaceDescription {
Identifie name;
RepositoryID id;
RepositoryID defined_in;
sequence fOpeationDescription operation;
sequence AttributeDescription attributes;
};
FullInterfaceDescription describe_interface();
};
Subsequently, the function InterfacDef::describe_interface() can be used to get a description of its constants, typedefs, exceptions, attributes, and operations. This call will return a FullInterfaceDescription structure defined inside the InterfaceDef object as shown above.
The list of parent interfaces is stored inside the attribute base_interfaces in the InterfaceDef object.
The list of attributes, together with all of information related such as name, type, and mode are stored in the attribute attributes in the InterfaceDef object. The structure of an AttributeDescription object is:
interface AttributeDescription {
Identifier name;
RepositoryID id;
RepositoryID define_in;
TypeCode type;
AttributeDef::AttributeMode mode;
// normal or readonly
};
The list of operations, together with all of information related such as name, return type, parameters, and mode are stored in the attribute operations in the InterfaceDef object. The structure of an OperationDescription object is:
interface OperationDescription {
Identifier name;
RepositoryID id;
RepositoryID define_in;
TypeCode result;
OperationMode mode;
sequence contexts;
sequence parameter;
sequence exceptions;
};
These are also ParameterDescription, ExceptionDescription, ConstantDescription, TypeDescription, and other strucutures to store information for parameters, exceptions, constants, types, and others respecitively. I am not going to describe them all here. For detail information, please refer to the Orbix Advanced Programmer Guide [5].
All the attributes will also be created under the definition of the local objects. However, I put a `.' before all the names of attributes. So, if the attribute is called "a1" in the CORBA interface, it will be called ".a1" in the local definition of CLIPS. It is to avoid problems because CLIPS has more reserved words. For example, the string "name" is a very common attribute in objects, but it is also a key word in CLIPS. The mode of an attribute can either be "read-only" or "read-write", the tool will maintain the same mode in the local object definition also.
There is a new attribute in every local object that correspones to a remote CORBA object. This attribute is called CorbaObjectNum, which is a mapping between a local object and a remote object. The use of it will be shown in the example section 4.7.
All the methods will be created in local object definitions under the same name as in the remote objects.
Please note that if an object with the same name has been defined in CLIPS already, then it will not be redefined again, not even under a different name.
Lastly, CLIPS supports fewer than data types than CORBA, so we make those data types in CORBA which are not in CLIPS to be mapped some other compatible CLIPS types. The mapping is shown here:
CORBA TypeCode CLIPS Data Type
tk_null NIL
tk_void *no suitable type*
tk_short INTEGER
tk_long INTEGER
tk_ushort INTEGER
tk_ulong INTEGER
tk_float FLOAT
tk_double FLOAT
tk_boolean INTEGER
th_char SYMBOL
tk_octet *no suitable type*
tk_any multislot
tk_TypeCode INTEGER
tk_Principal *no suitable type*
tk_objref the object name
tk_struct multislot
tk_union multislot
tk_enum multislot
tk_string STRING
tk_sequence multislot
tk_array multislot
Note that multislot is not a datatype in CLIPS. It means it makes the attribute to contain more than one element, with each element can be of different data type.
To construct the request in CLIPS, the user needs to supply a multivalued list in which the first value will be the return type. Next, if the return type is OBJREF, the second and third values will be the object type name and the new object instance name that will be created as the operation returns. Then the type and the value of every parameter will follow. Again, it will be more clear in the section 4.7 example.
extern "C" {
}
which tells that everything inside of the two brackets is in C object code.
Please refer to Appendix A for a list of the header files that needs to put the 'extern' around the function definitions.
4. Finally, we compile main.C, corba.C, with libClips.a, and the clips files with modified header files into a final running program.
// a simple description of a bank account
interface account {
readonly attribute float balance;
void makeLodgement (in float f);
void makeWithdrawal (in float f);
};
// a bank simply manufactures accounts
interface ir_bank {
exception reject {string reason;};
account newAccount (in string name) raises (reject);
currentAccount newCurrentAccount (in string name,
in float limit) raises (reject);
void deleteAccount (in account a);
};
First, the CLIPS user needs to get all the interfaces from the CORBA server.
CLIPS> (get-corba-interfaces "whale")
"CORBA objects created successfully!"
This call is to get all the interfaces from the CORBA server "whale".
The user can then see the objects have been defined after this call by using the CLIPS browse-classes function.
USER
INITIAL-OBJECT
account
currentAccount
ir_bank
Assume that only three interfaces are registered in the CORBA server. This shows that currentAccount is a sub-class of account.
Then the user wants to create an object of ir_bank.
CLIPS> (make-corba-instance "ir_bank" "bank1")
"Object created successfully!"
The name of the object supplied by the user is bank1.
Through the full description of the interface, which is also stored in the object, the user knows that there is a "newAccount" operations which will return an "account" object. The input parameter is a string.
CLIPS> (invoke-corba-operations
(send [bank1] get-CorbaObjectNum)
"newAccount"
(explode$ "OBJREF account a1 STRING marcus")
)
"CORBA function invoked successfully!"
a1 as an instance of object account is created.
(send [bank1] get-CorbaObjectNum) is to get the index into the CORBA
object array to get the object reference.
"newAccount" is the operation the user wants to invoke on the object.
(explode$ "OBJREF account a1 STRING marcus") is to create a multifield
value. This multifield stores the information of the return type of the
operation and information about the parameters. OBJREF signifies that the
return type of the newAccount operation is an object, which is of type
account (the second value). The name the user gives to this return object
is a1. There is one parameter, which is a STRING with value "marcus".
In order to check that the return account object really exists. The user can use the function instances to see the objects the user has created.
CLIPS> (instances)
[bank1] of ir_bank
[a1] of account
For a total of 2 instances.
This shows the bank1 object the user created explicitly and the a1 object, which is created as the call to function "newAccount" returned.
CLIPS> (send [a1] get-CorbaObjectNum)
1
This shows that a1 corresponds to the CORBA object 1 in the array of all the CORBA objects created. This is because bank1 takes the object number 0.
Next, the user sees that from the interface of "account", there is a makeLodgement operation.
CLIPS> (invoke-corba-operations
(send [a1] get-CorbaObjectNum)
"makeLodgement"
(explode$ "VOID FLOAT 12.34")
)
"CORBA function invoked successfully!"
Again, (send [a1] get-CorbaObjectNum) is to get the object number of a1.
"VOID FLOAT 12.34" means that the return value is void and there is only one parameter with type FLOAT and value 12.34.
Lastly, the user can check if the balance has been changed by using the following command:
CLIPS> (invoke-corba-operations
(send [a1] get-CorbaObjectNum)
"_get_balance"
(explode$ "FLOAT")
)
Result is 12.34
"CORBA function invoked successfully!"
The result "12.34" matches the lodgement the user made before.
Note that before executing the program, the user has no knowledge about the interfaces of the objects in the CORBA server and all the operations. The user only knows the host name of the CORBA server. All the CORBA operations are also performed remotely on the CORBA server dynamically.
- To use the Interface Repository, programs that make calls to the Interface Repository must be linked with the IRclt library.
- The Interface Repository must be installed.
- The default timeout of a connection is 10 seconds. In normal situation, it should be adequate. However, this tool remains connected to the CORBA server after the user has made to first call to the objects. After 10 seconds, if the user wants to make another call to the server, it will fail. This is undesirable. We want user to invoke operations anytime as long as the user is still in CLIPS. Therefore the server must change the default connection timeout period by the following function:
unsigned long connection Timeout(unsigned long, Environment &env = default_environment);
interface PrintServer {
void PrintPostscript(in file theFile)
}
interface WordServices {
void TranslateWordToPostscript (in file wordFile,
out file postscriptFile)
}
A similar tool can be made in CLIPS also, whcih makes use of the expert system capabilities provided by CLIPS.
However, in order to make CLIPS a CORBA server, the CLIPS source code must be modified a lot because there are some rules and steps that the server needs to take in order to be a CORBA server. Things get more complicated here since CLIPS is actually written in C.
Another area of interests is to expose CLIPS into the Microsoft Object Linking and Embedding (OLE) environment. OLE is a unified environment of object-based services with the capability of both customizing those services and arbitrarily extending the architecture through custom services.
Right now, CORBA does not really have a complete security implementation, and so our system does not have any security scheme now. Everyone can access the information provided by the Interface Repository. If the server has registered itself under the Information Repository, then everyone can access use its service. In order to make this tool useful in a real world, a complete authentication, authorization, and access control scheme must exist. Users can only access what they are supposed to access; information can only be exposed to those who have privilege to retrieve; users can prove their idendities to servers so that they can invoke retricted operations; and servers also have a way to prove their idendities to clients.
After fully security services are provided in CORBA, the tool must be modified to include all the authentication, authorization, and access control functions.
[2] Software Technology Branch, Lyndon B. Johnson Space Center, "CLIPS Reference Manual: Volume II Advanced Programming Guide, CLIPS Version 6.0", 1993.
[3] The DARPA Knowledge Sharing Initiative External Interfaces Working Group, "Specification of the KQML Agent-Communication Language", 1994.
[4] N. Singh, M. Gisi, "Coordinating Distributed Objects with Declarative Interfaces", March 1995.
[5] "Orbix Advanced Programmer's Guide", IONA Technologies Ltd., 1995.
[6] R. Orfali, D. Harkey, J. Edwards, "The Essential Distributed Objects Survival Guide", John Wiley & Sons, Inc., 1996.
[7] M. Weiss, A. Johnson, J. Kiniry, "Distributed Computing: Java, CORBA, and DCE", Open Software Foundation Research Institute, 1996.
argacces.h classcom.h commline.h cstrcpsr.h evaluatn.h expressn.h exprnops.h exprnpsr.h extobj.h inscom.h msgpsr.h multifun pprint.h prcdrpsr.h prntutil.h router.h scanner.h strngrtr.h symbol.h sysdep.h