cObject
---cConnection
Handles connections for drivers that support Connection IDs. We refer to this as using Managed Connections.
Read extended information about Managed Connections.
Basically this class, which is represented as a single global object, allows you to:
1. Add Connection ID / Connection String pairs
2. Use those connection IDs in your table INT files
3. Login to database servers via Connection IDs
4. Make embedded SQL (ESQL) connections to servers via Connection IDs
The cConnection class provides a complete interface, which includes:
- Adding/removing/editing connections
- Login/Logout via connection IDs
- Handling encrypted credentials (i.e., user names and encrypted passwords)
- Dynamic switching of connection string definitions while an application is running
- Providing a simple high level interface for basic usage
- Provide a complete lower level interface for advanced usage
- Integration with the cApplication to further simplify high level coding
- Providing a more standard approach for dealing with SQL where you:
1. Define a server/database connection
2. Log in to an SQL server and database
3. Open and access tables in that database
At the simplest level, managed connections consist of:
1. Create Connections INI file (using the Studio's Manage Connections dialog)
2. Configure your tables to use managed connections (using the Studio's Connection Wizard)
3. Add a few lines of standard code to your application
a. Use the cConnection package
b. Create a cConnection object and place it above or inside your cApplication object
c. Add a login encryption object package and a database login dialog package inside or above the cConnection object.
4. Compile and run your application. The cApplication object will automatically register and login all managed connections.
Use cConnection.pkg : Object oApplication is a cApplication Set psProduct to "DataFlex Examples" : Object oConnection is a cConnection Set pbLoginOnOpen to False Use LoginEncryption.pkg Use DatabaseLoginDialog.dg // (Windows applications only) End_Object End_Object
Windows Applications, by default, include a connection dialog (included via "Use DatabaseLoginDialog.dg"). Since Web Applications cannot display Windows dialogs and the DataFlex Web Application Service typically uses a different Windows user id than your Studio developer Windows user id, especially when deploying an application, we have added a Windows application template in the Studio under File > Create New > Project named "Managed Conenction Login Project". This creates a small Windows application that allows you to set the credentials for Managed Applications. Once it has been run, you can then run a Web Application in the same workspace to use those credentials.
Typically there will be a single global cConnection object. When the object is created, the system global variable ghoConnection is set to its object handle. This allows other parts of the framework to communicate with the cConnection object without knowing its name and location. Developers should use this handle when communicating with the cConnection object.
Because a global handle is used, it does not really matter where the cConnection object is located as long as it is created before it is used. As a general rule it should be created before or inside of your cApplication object.
If pbAutoConnect is True, AutoConnect registers all connections by sending RegisterAllConnections and logs in to all connections by sending LoginAll. If either of these messages fail, the application is aborted.
The cConnection object should be placed inside of or before the cApplication object. If you are not using managed connections, you don't need it, although having it won't cause any problems. If a developer wishes to create the cConnection object inside of their cApplication subclass, they may do so. This was not done in the packages, because it makes it harder to customize the cConnection object via property settings and events.
Here is the recommended object placement for cConnection objects:
Object oApplication is a cApplication Set psProduct to "DataFlex Examples" : Object oConnection is a cConnection Use LoginEncryption.pkg Use DatabaseLoginDialog.dg End_Object End_Object
When used this way, an application using managed connections will register all connections and log in to all connections. This will occur immediately after the workspace has been selected and opened. If the register or login fails, the application will abort. If registered connections are not being used, this code will work exactly as before.
Here is a quick summary of these requirements:
1. Object oConnection: The connection object should be placed inside of or before the cApplication object. The oConnection object is optional.
2. Use LoginEncryption.pkg: The Login Encryption object, if used, must be created before a connection is added (registered). The encryption object must be created before the cApplication object's End_Object. Placing it inside of the cConnection object is the recommended location.
3. Use DatabaseLoginDialog.pkg: The database login dialog, if used, must be created before a connection is logged into. The login dialog object must be created before the cApplication object's End_Object. Placing it inside of the cConnection object is the recommended location.
Using connection IDs makes it easier to use Embedded SQL (ESQL). Once a connection is created and logged into, you may obtain an SQL connection handle by calling SQLConnectionId, passing the connection ID. This lets you create SQL statements without having to worry about connection server strings and logins.
Get SQLConnectionId of ghoConnection "ID1" to hoConnect Get SQLOpen of hoConnect to hoStmt Send SQLExecDirect of hoStmt "select * from salesp" Get SQLFetchResultsetValues of hoStmt to Mydata Send SQLClose of hoStmt Send SQLDisconnect of hoConnect
Note that the connection must be registered and logged into before it can be used.
In addition to SQLConnectionId, the message SQLConnectionByTable may be used to create a connection handle for the connection being used by an opened table.
Underneath this simple interface are at least two levels of interfaces that can be used to handle custom conditions. For example, our tools (Studio, etc.) will use the cConnection object, but will have to use a lower level interface to handle its more dynamic application requirements. In the Studio, we need to know when to register connections, login to servers, open tables, close tables, logout of connections, remove connections and then start over with a new set of connections. The interfaces exist to do all of this.
While we hope that developers will not have to dig too deeply into these lower level interfaces, if needed they are there. Just about anything can be customized.
Before you use a managed connection, you must define and initialize that connection. Most typically this is done by reading the connections INI file and registering all active connections in this file. There are a variety of public interfaces that can be used to add a connection.
A login occurs after the connections are registered. This should occur before tables using the connection are opened. Most typically this is done by sending a message to log in to all registered connections. There are a variety of public interfaces that can be used to log into a connection. We will start with the highest level and work our way down.
When a connection is registered, it may be passed login credentials (user name, password and a trusted-connection flag). When adding a connection these credentials should be passed as separate parameters (the exception being a DSN that may already contains these credentials in the DSN).
Normally, this information is stored in the connections INI file. The password will be encrypted. The credentials are read and decrypted as part of LoadStoredConnections. The credentials are encrypted and written by sending the message StoreConnectionIdCredentials. Augmentable interfaces exist for encrypting and decrypting passwords. Augmentable interfaces exist for reading and writing credentials to the connections INI file.
You can login out of any or all connections. You can remove any or all connections. In a normal application there is no need to do this, but it can be done. You might need to do this if you want to remove a current set of connections so you can register and log in to a new set of connections.
Note that there is a hierarchy of registering connections, logins and opens. A connection must be registered before it can be logged into. A connection must be logged into before a table can be opened. When a connection is logged out of, all open tables will be closed. When a connection is removed, all connections are logged out (and therefore tables are closed).
Changing connections refers to changing existing server connections. When you do this you are keeping your connection ID the same (the ID must always be the same) and you are changing your connection server-string so your data can be accessed from a different location. This can occur in a number of different places during an application's lifetime - before the application launches, as part of application start up or while an application is running. No matter where this occurs, one important restriction applies - the definitions of the tables being accessed from the different sources must be the same. The server strings (which contain a DSN or a Server plus a database) and the login credential can change but the table's back end definition as defined in the table INT file must be the same. It is your responsibility to make sure that this requirement is met.
See Changing Connections for more information.
There are numerous methods for managing connection information:
Each managed connection has a driver. When a connection is added, its driver is loaded and registered as needed. Once a driver is loaded and registered, it is never removed. This means that you normally do not need to concern yourself with loading drivers as adding a connection will do this for you.
Sometimes you need to send API messages to the driver itself. You tend to do this by sending a message to a driver CLI handler or by sending an API message to the driver via an index. The cConnection interface has interfaces to assist in this process.
The cConnection object has its own error handling. It traps errors in areas where things are likely to go wrong and identifies three kinds of errors:
A configuration occurs when a cConnection error occurs because something is wrong with configuration. Often this will be a problem with the connections INI file (bad INI format, missing keywords, missing connection ID, etc.) but it can also occur when the driver is bad or missing. When this happens there is probably nothing wrong with your code. These are problems that need to be solved at the install and configuration level.
When a configuration error occurs the ConfigurationError message is sent.
An interface is provided that traps errors. It does this by surrounding code that might fail (like loading a driver) with a trap start and end message. After the error trapping is ended, you can check if an error occurred and handle it as desired. This interface is used by many of the methods in the class and can be used by the developer as well.
Here is an example of how error trapping is used:
// low level load which does not register the driver - only loads { Visibility=Private } Function LoadDriver String sDriver Returns Boolean Integer iErrorNumber Send TrapErrors Load_Driver sDriver Send UnTrapErrors Get piErrorNumber to iErrorNumber Function_Return (iErrorNumber=0) End_Function
After the cApplication opens a workspace, it has the ability to auto-connect to managed connections. If the cConnection object exists (ghoConnection is non zero), it will send the message AutoConnect to the connection object. If the connection object's pbAutoConnect property is true, an auto-connect will be attempted. The connection object will register all connections (Send RegisterAllConnections) and login to all connections (Send LoginAll). If either of these processes fail, the application will be halted.
This process is instigated within the cApplication DoOpenWorkspace method, which is the main interface used to open a workspace. DoOpenWorkspace is most often called when the cApplication object is created - either in OnCreate or by the object's end-constructor. This is the process that automatically opens a workspace. When a workspace is automatically opened, managed connections can be automatically connected.
This auto-connect behavior can be disabled by setting pbAutoConnect to False in the cConnection object. If you wish to customize the auto-connect process, you may augment the cConnection AutoConnect procedure. In addition, a cApplication event named OnWorkspaceOpened is sent, which could also be augmented to handle custom needs.
Because the cApplication object sends messages to the cConnection object, the cConnection object must be created before or inside of the cApplication object.
The LoginEncryption.pkg creates an object that is used to manage login password encryption and decryption. It determines the rules for password encryption. It can be replaced with a custom package of the developer's choosing.
The cConnection's pbEncryptPassword property determines if a password encryption is used. If true, an encryption object is required. If false, no password encryption is needed. If false, the encryption package is not required and may be removed from your code. When the encryption object is required, an unhandled programming error will be raised if the encryption object is missing or not properly defined.
The cLoginEncryption object must be created before a connection is added. If connections are created automatically by the cApplication object, this means that the encryption object must be created before the cApplication object's End_Object. Placing it inside of the cConnection object is the recommended location.
DatabaseLoginDialog.dg defines the database login dialog that pops up if the stored login credentials are incomplete (i.e., if a silent login fails). This will mostly be used one time to configure and store your database login credentials. Once a valid password is stored, you'd never see this dialog. If this dialog is not included, a password dialog will not appear and a failed database login will result in a failed login, which will result in an application abort.
The pbLoginDialogRequired property determines if a database login dialog is required. If true, a login dialog object is required. If false, the dialog (even if present) is not used, which means that a failed silent database login will simply fail. If the database dialog is required, an unhandled programming error will be raised if the object is missing or not properly defined.
The connect interface can be extended at the cConnection object level or class level. The cApplication object can also be extended to change how it works with managed connections.
Read extended information about Managed Connections.