Client Handle
Most applications do not need to create explicit client handles. The global application object, which is created when starting up an ODABA application, is usually sufficient for opening a database. Only when specific or multiple server connections are required, client handles have to be provided for each server connection, but not for each database on the server.
The client handle guarantees the scalability for ODABA applications, since just by configuring the client in different ways, one may run the application as local, file server, object server or as replication server application.
The configuration of the client/server mode for the application is provided in a configuration file (traditional ini-file or via an xml option file) or in a data catalog. Details about defining data sources are described in the "Database Reference" manual.
Most applications work with one client, which is required only, for opening the dictionary handle(s). Complex client/server applications communication with different databases on different servers may, however, require several client objects. Several client objects might also become necessary, when running multiple thread applications in order to provide a separate server connection for each thread.
There is a big difference between client handle and a client object. The client handle is not much more than a managed pointer to a client object. Thus, many client handle may refer to the same client object. ODABA tries to minimize the number of client objects, i.e. the number of connections to the server. Simple applications use one client object only, which is called the system or main client. Multiple connection applications will implicitly create a number of client objects managed by ODABA.. But when the application becomes very complex concerning the connections to different servers, the application may implement their own client manager.
The first client created in an ODABA application is considered to be the main client. The main client handle is used, whenever a client handle is required but not set or passed explicitly.
When initializing the application by calling Application ::initialize() , the main client is automatically created for connection the SYSTEM data source. When the data source refers to a database on a server, the main client automatically connects to this server.
Otherwise, the main client is created implicitly when creating the first Client object. The main client can be provided simply by calling Application ::mainClient() . Creating a default data source (without client reference), the data source uses the main client. When no main client has been created so far, calling Application :: mainClient() will automatically create one.
Empty main clients need not explicitly connect to the database, when running in local or file server access mode. Running, however, in client/server mode, empty client handles must be connected explicitly to the server.
// open local dictionary with application's main client
Dictionary dict("C:/ODABA/Sample/Sample.dev", Read, true);
// empty client handle opens local dictionary
Client client;
Dictionary dict(client, "C:/ODABA/Sample/Sample.dev", Read, true);
// client handle opens server dictionary
Client client;
Dictionary dict;
client.Connect("my_server", 6123);
dict.open(client, "%SAMPLE_DICT%", Read, true);
When running an application in client/server mode, each client can manage maximum one connection to a server. Thus, when running an application communicating with several servers, several clients must be created. Each server connection allows accessing any number of dictionaries or databases provided on the server.
Scalable (initialized) client handles can be created also by constructing an empty client and initializing it.
Scalable clients work fine in local and client server mode as long as all data sources requested by the application are running locally or on the same server. A client may serve at the same time local or file server data sources, but not data sources residing in different servers.
For handling different servers or multiple server connections, you need a client handle for each server connection.
// scalable client handle
client sam_client("c:/ODABA/Sample/Sample.ini", "Test Application");
When a main client had been created, the client can be initialized later on by calling Client::reopen() . The client must be initialized before opening a data source or connecting to a database. Initializing a client should not be done while the client is connected, but initialization can be redone, after the client has been disconnected.
Initialized clients need not explicitly connect to the database (server). The connection is established automatically, when opening the first data source for the client.
ODABAClient client();
...
client.Initialize("c:/ODABA/Sample/Sample.ini", "Test Application");
When referring to data source definitions defined in a data catalog or in an configuration file, the application need not care about server connections or local database access.
More complex applications requesting databases from different servers need an own connection management in the application or a well-organized set of configuration files. Since configuration files keep the application free from knowing anything about the client/server mode, this is the suggested way to handle multiple server connections.
One could create a client for each data source requested in the application, but when the application accesses several data sources residing on the same server, it is more efficient, using only one connection for all this data sources. The best way is to put all data sources for one server in the same configuration file and creating a client for each configuration file. Still the application needs to know, which data source is defined in which configuration.
Instead trusting the automatic connection feature of the client handle, one may manage the server connections by your own in the application.
// connection manager (draft)
bool openConnection(DataSourceHandle &dsh, ClientPool &cp) {
Client *current_client = 0;
if ( !(current_client = cp.Locate(dsh.server_name)) ) {
current_client = new Client();
if ( current_client->connect(dsh.server_name, dsh.server_port) ) {
printf("server %s(%i) not accessable", dsh.server_name, dsh.server_port);
delete current_client;
current_client = NULL;
} else {
cp.AddClient(dsh.server_name, current_client);
dsh.Open(*currentClient);
}
}
}