The previous chapter discusses the basic requirements for a client application that uses Caché Direct. This chapter discusses how you can specify custom processing to perform at various times of the overall client/server interaction. Many of these features are hooks included in specific parts of the typical client/server interaction; most of these are specific to the server. The client has some additional features as well.
This chapter contains the following sections:
This section provides an overview of the client/server interaction and the server behavior in general. First, the typical client/server interaction consists of the following steps:
The client connects to the server and sends a NewTask message to it.
The client then sends a BeginTask message.
If no license slot is available, the server returns a <No License> error and disconnects.
The client then typically sends any number of ExecuteCode messages. Just before the client sends each message, there is a client-side write hook
where you can add your own processing.
The server receives the message, performs any processing specified in the server read hook (ReadHook
) and then reads the message.
The server executes the code as required.
The server performs any processing specified in the server write hook (WriteHook
) and then sends the message.
At the end of the interaction, the client sends an EndTask message, which breaks the connection.
The server receives the EndTask message and then checks the EndTaskHook
, to which you can add your own processing for the server to execute.
In contrast to your custom EndTask processing, your custom shutdown processing also
occurs outside of the client/server interaction that is described here. The shutdown processing occurs if the server shuts down for any reason, such as a timeout failure.
As you can see from this description, Caché Direct provides hooks that you can use to insert processing just before messages are sent (a write hook) and just after they are received (a read hook). You can use these hooks for such tasks as custom logging, compression, encryption, or any other purpose chosen by the application. The write hook on the client is paired with the read hook on the server, and vice versa. If a write hook transforms a message, the corresponding read hook should ensure that the message is back in the format expected by Caché Direct.
The server traps any nonfatal errors and reports them to the client, via the following procedure:
Send a message to the client, passing the current values of the server local variables error
error hook is a local variable on the server (that is, within an individual job). To use it, you set it equal to a string that contains a routine name in the form label^rtn
. The server evaluates the variable when an error occurs. If the variable is not empty, the server then calls the routine. If the variable is empty, the server continues to its next step.
You use this hook to specify additional error processing. For example, you can extend the error message. Your function can use the local variables error
in any way, provided that it does not render them unusable by the rest of the error-handling routine. (That is, the variables must still be defined and must still be text and a number that fits in two bytes, respectively). For example, your code could change or expand the error number or text for better use by the client application code.
In a client/server application, there are many ways for the client and server to lose connection with each other. Caché Direct provides a way for the client and server to periodically check the connection and to respond appropriately if it has been lost. For the server, shutting down gracefully is the only meaningful response. For the client, however, you might want to establish a new server connection, for example, or display a message for the user.
This feature includes the following elements:
Initial Keep Alive Interval
The server shuts down if it has not received a message from its client within five keep alive cycles. When the server starts up, its initial KeepAliveInterval
is 17280 seconds (1/5 of a day). So, by default, the server will shut down if it has not heard from its client in five keep alive cycles of 17280 seconds each (a total of 86400 seconds/24 hours). A lower setting like 300 (5 minutes) would usually be reasonable.
Keep Alive Settings (Client)
The client controls the keep alive interval and resends this value each time it sends a keep alive message to the server. The VisM has two properties that control the keep alive behavior:
The KeepAliveInterval property
specifies the communication idle time in seconds, for the purpose of the keep alive mechanism. When no client/server communication has occurred for this many seconds, the client sends a keep alive message to the server. If the client does not receive a reply within the period specified by KeepAliveTimeOut
property, the client fires its ShutDown event. If it does receive a reply, it waits again and sends another keep alive message.
property specifies a timeout for the keep alive round trip, which should be shorter than the general timeout period, TimeOut
Shutdown Event for Keep Alive Failure (Client)
If the client does not receive a response to its keep alive message as noted previously, the client then does the following:
This property always indicates the state of the connection. If ConnectionState
is a zero, the connection is OK or a successful disconnect has occurred. If the property is nonzero, then it indicates the time of day (in seconds since midnight) when the server was lost. (This is the same as the second piece of $Horolog
. The day is not indicated; it is presumed to be recent.)
It then fires the ShutDown event, passing one argument to this event, namely, the current value of the ConnectionState
If the client attempts to send a message after the connection is lost, a <ServerLost> error results.
This event is triggered only if the client does not receive a response to its keep alive message as noted previously.
The Server Read Loop and Quit Check
After a Caché Direct server process has been established, it waits in a polling loop that begins with a timed read, listening for communication from the client. The read timeout is ten seconds. If the read is completed before the timeout, the server processes the client communication, writes the response message back to the client, and returns to the start of the polling loop. If the timeout expires first, however, the server follows a specific procedure to determine whether this server process should shut down, as described next.
Server Quit Check Procedure
If the read loop times out, the server follows a specific procedure to determine whether this server process should shut down. If the server determines that it should shut down, it calls the ShutDownHook
, performs any processing specified there, and then shuts down. Otherwise, it continues to the next step in the quit check procedure.
Several of the server-side hooks appear within this procedure, which is roughly as follows:
Call the function named by the local
idle-time hook (%cdPULSE
), and, if requested, perform idle-time processing. If the function returns 0, the server continues the server quit check procedure. If the function returns 1, the server shuts down.
Call the function named by the global
idle-time hook (IdleHook
), and, if requested, perform idle-time processing. If the function returns 0, the server continues the server quit check procedure. If the function returns 1, the server shuts down. For more information, see the section Other Server-side Hooks (Global Variables),
later in this chapter.
Calculate the amount of time since the last communication from the client. If more than five keep alive intervals have passed without any communication from the client, the server shuts down.
Check to see whether the system has received a shutdown signal. If so, the server shuts down.
Check to see whether the server has received the Stop^%CDSrv
command. If so, the server shuts down all Caché Direct server jobs.
Check to see whether the server has received the StopJob^%CDSrv
command. If so, the server shuts down the specified server job.
Check to see whether the slave server should quit. If so, the server shuts down.
At any step, if the check indicates that the server should shut down, the server calls the shutdown hook (ShutDownHook
), performs any processing specified there, and then
hook is present only as a local variable (that is, within an individual server job). To use it, you set it equal to a string that contains a function call in the form $$label^rtn
. The server evaluates the variable at a specific time as described in the previous section. If the variable is not empty, the server then performs the designated function call and quits if the result is not 0
. If the variable is empty, the server continues to its next step.
As noted earlier, Caché Direct provides hooks that you can use to insert processing just before messages are sent (a write hook) and just after they are received (a read hook). You can use these hooks for such tasks as custom logging, compression, encryption, or any other purpose chosen by the application. The write hook on the client is paired with the read hook on the server, and vice versa. If a write hook transforms a message, the corresponding read hook should ensure that the message is back in the format expected by Caché Direct.
Expected Message Format
The first four bytes of the message are a 32-bit integer that contains the length of the message. You must be sure to update this if you change the length of the message.
It is the responsibility of the programmer to ensure that the server and client routines correspond appropriately with each other.
Server-side Read and Write Hooks
You use this hook to specify processing that the server should perform right after it receives an ExecuteCode message. The server calls this hook as soon as it receives an ExecuteCode message from the client, before assigning values to the mirrored properties.
Your function uses the local variable named %cdMSG
, which contains the incoming message. The function should perform its actions and return the converted message. If the message was not changed, just quit: Quit ^%cdMSG
You use this hook to specify processing that the server should perform before it sends a response to the client. The server calls this hook just before it sends the message to the client.
Your function uses the local variable named %cdMSG
, which contains the outgoing message. The function should perform its actions and return the converted message. If the message was not changed, just quit: Quit ^%cdMSG
Client-side Read and Write Hooks
On the client side, you install the hooks by creating a DLL named CDHooks.dll
(for 32-bit systems) or CDHooks64.dll
(for 64-bit systems) in the same directory as the VisM.ocx
file, respectively. The hooks are entry points with specific names and signatures, namely:
unsigned char* ReadHook(unsigned char* pInMsg);
unsigned char* WriteHook(unsigned char* pInMsg);
void FreeHookMem(unsigned char* pMem);
Take an input argument that is a pointer to a block of bytes, the incoming message.
Return a pointer to a block of data.
Return a pointer to the newly allocated data. (In that case, the client will copy the new data block and then free what was returned with the FreeHookMem
If the return value is the same as the argument, then no memory will be freed. This case would be a situation where the data was either not changed or occupies the same space as the original message. It is your responsibility to write client routines that correspond appropriately with your routines on the server side.
routine is expected to free the memory allocated by the other routines.
Other Server-side Hooks (Global Variables)
This section provides reference information for other server-side hooks that are kept in a global variable.
All the hooks in this section use the same general mechanism. You specify a value for the global ^%CDSwitch("HookName")
, which should be a string that contains a function call in the form $$label^rtn
. The server evaluates the global at a specific time; see the Overview
section of this chapter. If the global is not empty, the server then performs the designated function call. If the global is empty, the server continues to its next step.
For all of these, execution takes place by indirection, as follows:
is the return value.
You use this hook for activities such as adjusting protection parameters or changing namespace for the process. The server calls this hook after creating the server job and before executing any code; see the Overview
section of this chapter.
Your function can use the following local server variables as arguments:
the name of the executable that is being run on the client, such as myapp.exe
the IP address of the client, in string form, such as 127.0.0.1
. This may or may not be useful, depending on how the client is connected. For example, a non-TCP Citrix connection receives an artificial IP address to satisfy the Caché licensing system.
Your function should return either:
Failure (a string of the form ^errornumber^errorname"
, where errornumber
may not be 0). The range 20900-20999 has been reserved in the Caché Direct error numbers for application-created errors. In the case of failure, the error number and text will appear in the VisM properties Error
, and the error event will be signaled.
You use this hook to specify server-side processing to occur when the server is otherwise idle, for example, when the polling read interval has timed out. The server calls this hook as part of its quit check procedure; see the section Server Quit Check Procedure
in the overview of this chapter.
You use this hook to specify any additional processing that the server should perform if it receives an EndTask message from the client. This hook allows for any application cleanup.
You use this hook to specify any additional processing that the server should perform when it shuts down for any reason. The server calls this hook whenever it shuts down.
The function should return either 0 (if the server should not shut down) or 1 (if the server should shut down).
When VisM sends a message to Caché, the server may take some time to process the message request, which leaves the user waiting for a response. If the delay is long, you can give users the option of canceling the request. To set this, you use the PromptInterval
property. This property specifies how long, in seconds, the client application should wait (if the server has not yet responded) before displaying a prompt to the user. This prompt would give the user the option of waiting longer or of canceling the activity. The MsgText
property is a four-piece string that specifies the message to use in this situation; for details, see the Other VisM Properties
section of the chapter VisM.ocx Control Details