JetNet/Oracle Tuxedo Guide


Chapter 6. JetNet/Oracle Tuxedo Event Processing

In JetNet and Oracle Tuxedo applications, events occur during the processing of service requests. These middleware events are one of several categories of events that occur in a Panther application. For information about the different types of application events, refer to Chapter 17, "Understanding Application Events," in Application Development Guide.

When middleware events occur, they are forwarded to handlers for processing. Panther provides built-in event handlers for all request broker event types. You can also write and install your own handlers in JPL or C. These handlers can perform all required processing on their own, or they can call the built-in handlers and overlay these with desired enhancements.

Table 6-1 lists all middleware event types that Panther recognizes in JetNet and Oracle Tuxedo applications:

Table 6-1 Middleware events

Event type Description Location

advertise

A service has been advertised

Server

exception

An error or unusual change in the normal flow of program execution

Client or server

jif_changed

The JIF has been changed

Client or server

message

A client receives an unsolicited message

Client

post_request

A service request is completed

Client or server

post_service

A service completes execution

Server

pre_request

A service request is initiated

Client or server

pre_service

A service is about to begin execution

Server

request_received

A service request is received by the server

Server

server_exit

A server is brought down in an orderly fashion

Server

unadvertise

A service has been unadvertised

Server

unload

Data is received from an external source that can be written (unloaded) to Panther variables

Client or server


Event Sequence

During the typical life span of a Panther enterprise application, most or all middleware events occur. Some of these, such as advertise events, happen independently of other events. For example, in an multi-server enterprise, servers can be activated and deactivated in a running application, thereby generating advertise and unadvertise events. In the meantime, clients continue to issue service requests, and those servers that are available process them; these actions spawn their own set of service-related events, such as request_received and unload.

Of all middleware events, only those that are connected to service requests are dependent on each other, and always occur in this sequence:

Action Events

Client initiates request

1. pre_request

Server receives request

2. request_received

3. pre_service

4. post_service

5. unload

Server returns service to client

6. post_request

Service returns to client

7. unload


Handler Scope and Installation

All middleware event handlers are installed at one of several scopes, which are hierarchically ordered from most general (default scope) to most specific (request scope). A handler can be installed at one or more scopes, depending on its event type. When a request broker event occurs, Panther looks for its handler at the most specific scope that is valid for the event. If no handler is installed at that scope, Panther continues to search for the event's handler among other valid scopes, each more general than the last, until it finds one.

Event handlers can be installed at four scopes, listed here in ascending order of precedence (general to specific):

For example, an unload handler can be installed at any scope. When an unload event occurs, Panther first checks whether an unload handler exists for the applicable service. If no request handler is found and the unload event occurred within a transaction, it checks whether an unload handler is installed for that transaction. If no transaction handler is found, Panther looks for a handler installed at application scope. If no application-scope handler is set, Panther uses the default unload handler.

If Panther cannot find the specified handler, a TP_HANDLER_MISSING exception of severity TP_ERROR occurs.


Writing Event Handlers

You can write a middleware event handler in JPL or C. Handlers written in JPL can be stored in screen or library modules; handlers written in C must be installed as prototyped functions. (For more information, refer to "Prototyped Functions" in Application Development Guide.)

Request broker event handlers that are written in JPL should be accessible to the application either as library modules that are made public (via the public command) or as screen modules (via the screen's JPL Procedures property):

Handlers for a request broker event type must conform to a contract which is specific to the event type, and specifies the handler's implementation. A handler contract specifies the number and type of parameters, and how to interpret its return integer value. For information about handler contracts, refer to event type descriptions later in this chapter.

Events Generated within Handlers

An event handler should avoid generating events of its own type as this can create an infinite loop. Because pre_request, post_request and unload events are necessary to make service requests, these events can be generated recursively. Handlers for these events should guard against generating events of the same type.

When a handler is invoked for an event, the following changes to normal processing occur:


Built-in Handlers

The Panther distribution provides a number of built-in handlers that are internally installed. All handlers at default scope are built-in handlers. A number of the built-in handlers are not used as defaults; these are available for installation at other scopes. For example, two built-in handlers are available for jif_changed events: the default handler sm_tp_jif_changed_read, responds to any change in the JIF and readvertises its services; sm_tp_jif_changed_ignore ignores all changes. You might want temporarily to install sm_tp_jif_changed_ignore for a deployed application so it is unaffected by updates to the JIF, such as addition of new services that are undergoing development.

Some event types use different built-in handlers as the defaults for production and development environments. Default development handlers are called by standard servers that are configured for development; default production handlers are called by standard servers that are configured for production. For example, sm_tp_advertise_cond_winopen is the default handler for advertise events on a server that is configured for production; this handler conditionally caches in memory the service components of advertised services. A server that is configured for development uses sm_tp_advertise_log; this handler never caches service components, and also posts success messages to the request broker's log file.

Table 6-2 lists all built-in handlers and indicates which ones are used as defaults for development (dev) and production (prd). Descriptions of these handlers can be found in the event type descriptions that follow.

Table 6-2 Built-in event handlers and defaults

Event Handler

advertise

sm_tp_advertise_cond_winopen (prd)

sm_tp_advertise_ignore

sm_tp_advertise_log (dev)

sm_tp_advertise_winopen

exception

sm_tp_exception_no_change

sm_tp_exception_print_all (dev)

sm_tp_exception_print_warning

sm_tp_exception_promote_error (prd)

jif_changed

sm_tp_jif_changed_ignore

sm_tp_jif_changed_read (dev/prd)

message

sm_tp_message_ignore

sm_tp_message_print_string (dev/prd)

post_request

sm_tp_post_request_ignore (dev/prd)

post_service

sm_tp_post_service_ignore

sm_tp_post_service_winclose (dev)

sm_tp_post_service_winclose_or_deselet (prd)

sm_tp_post_service_windeselect

pre_request

sm_tp_pre_request_ignore (dev/prd)

pre_service

sm_tp_pre_service_ignore

sm_tp_pre_service_winopen (dev)

sm_tp_pre_service_winopen_or_select (prd)

sm_tp_pre_service_winselect

request_received

sm_tp_request_received_ignore (prd)

sm_tp_request_received_jif_check (dev)

server_exit

sm_tp_server_exit_ignore

sm_tp_server_exit_log_down (dev/prd)

unadvertise

sm_tp_unadvertise_cond_winclose (prd)

sm_tp_unadvertise_ignore

sm_tp_unadvertise_log (dev)

sm_tp_unadvertise_winclose

unload

sm_tp_unload_immediate

sm_tp_unload_call_origin (dev/prd)


Advertise and Unadvertise Events

advertise and unadvertise events occur on the successful return of the advertise and unadvertise commands, respectively. Both events occur only on servers.

advertise and unadvertise can specify a single service, a group of services, or all services. Each service generates its own set of advertise and unadvertise events.

Advertise and Unadvertise Handlers

advertise handlers are used to perform service initialization. unadvertise handlers are used to perform service cleanup.

Scope

advertise and unadvertise handlers can only be installed at application scope.

Contact

advertise_handler(char *cmdStr, char *serviceName,
   char *groupName, char *serviceContainer, int cacheScreen)
unadvertise_handler(char *cmdStr, char *serviceName,
   char *groupName, char *serviceContainer, int cacheScreen)
cmdStr

The advertise and unadvertise handler arguments.

serviceName
The name of the service as specified in the JIF.

groupName
The name of the service group specified in the advertise or unadvertise command. If none is specified, this argument is set to null string.

serviceContainer
The name of a service component associated with the service, as specified in the JIF.

cacheScreen
Determines whether to cache the service component in memory and so determines how the handlers access serviceContainer, with one of these values:

Returns

The return codes from advertise and unadvertise handlers are ignored; a 0 return value is suggested.

Built-in Handlers

Panther provides four built-in advertise handlers:

Panther provides four built-in unadvertise handlers:


Exception Events

An exception event occurs while the middleware API is performing work on behalf of the application. All request broker commands can generate one or more exception events. Not all exception events are errors; however, all request broker errors are exception events.

Each exception event is associated with an integer code, string constant, and message, which are set in application properties tp_exc_code, tp_exc_names and tp_exc_msg, respectively. tp_exc_names is an array of all exception event type constants that are indexed by the corresponding exception codes. tp_exc_msg contains a string that describes the latest exception event.

For example, this JPL statement displays a message that describes the latest exception event to the user:

msg emsg "Error: " ## @app()->tp_exc_names[tp_exc_code] \ 
## "%NMessage: " ## @app()->tp_exc_msg

Each exception event sets tp_severity to a severity code, which is also supplied as an argument to the exception handler function.

For a complete list of exception event type constants, refer to page D-1. For exception severity codes refer to page 6-13.

Exception Handlers

When an exception event occurs, its handler performs the required processing. This processing might include displaying a message to the user, cancelling a request, or rolling back a transaction.

On entering an exception handler, Panther sets these application properties:

For example, the following exception handler alerts the user of an error condition whose severity level is equal to or greater than TP_ERROR:

proc err_print (cmdStr, cmdName, callid, severity)
	if ( severity >= TP_ERROR )
		msg emsg "Error: " @app()->tp_exc_code \
"Message: " @app()->tp_exc_msg
	return severity

The handler should store the value of tp_exc_code and/or tp_exc_names before it calls any other middleware API-related functions; otherwise these properties are liable to be reset before control returns to the handler. Similar precautions are unnecessary for the handler's severity code, because the severity level is already locally available as a handler parameter.

Exceptions within an Exception Handler

If an exception handler generates its own exception events, Panther ignores them and does not call any handlers for them, and the tp_severity property remains unchanged. To catch exceptions within an exception handler, check the tp_severity property after each command in the handler. The original handler then decides whether errors within the handler should affect the severity that it returns.

Scope

exception events can occur on clients and servers. exception handlers can be installed at all scopes.

Contract

exc_hdl(char *cmdStr, char *cmdName, char *callid, int severity)

cmdStr
The entire text of the JPL command and arguments that generated the exception event.

cmdName
The name of the command only, stripped of any arguments.

callid
The identifier of the service call for which the command was issued. If no service call applies, callid is an empty string.

severity
The default severity for the exception event.

Returns

An exception handler must return a valid severity code. This code is written to the tp_severity property, and determines how the application responds to the exception event. If the handler returns an invalid return code, tp_severity is restored to the severity level it had on entry to the handler. For a description of all exception severity codes, refer to page 6-13.

Warning: If a JPL procedure omits a return statement, it returns a value of 0. exception handlers that are written in JPL should always explicitly return with the appropriate severity code.

Exception Severity Codes

All exception event handlers must return a severity code. Each exception event has a default severity, and the tp_severity property is set to the corresponding code on entry to the exception handler. The default severity code is also supplied as an argument to the handler function. If a request broker event generates multiple exceptions, the exception with the highest severity has precedence and sets tp_severity.

The following sections lists severity codes in ascending (lowest to highest) order and describes what action the application takes when a handler returns one of them.

TP_NONE
The exception event is ignored. Processing of the command or request continues and the tp_exc_code property remains unchanged. The status of an enclosing transaction is unaffected.

TP_INFORMATION
Processing of the command or request continues. The status of an enclosing transaction is unaffected.

TP_WARNING
Processing of the command or request continues from where the event occurred. The status of an enclosing transaction is unaffected.

TP_ERROR
Processing of the command or request continues, but at the next processing stage, if any. If no processing stage follows, the command terminates. If the exception occurs within a transaction, the transaction's status is set to

TP_WILL_ABORT.

TP_MESSAGE
exception events of this severity are only raised during the process of exchanging data between clients and servers. An exception of this severity level causes the send/receive process to abort any further attempt to send/receive that message. Any other processing associated with that message is also aborted. If the exception occurs within a transaction, the transaction's status is set to TP_WILL_ABORT.

TP_COMMAND
The function or command associated with this severity terminates. No further messages are sent or received. If the exception occurs within a transaction, the transaction's status is set to TP_WILL_ABORT.

TP_REQUEST
Applicable only to exceptions that occur while a service request is being processed. Otherwise, a TP_REQUEST severity is reduced to a TP_COMMAND severity. For a client, all processing associated with the request is terminated.
After the request aborts, a post_request event occurs. The value of the tp_exc_code property is set to the exception code. The status of an enclosing transaction is set to TP_WILL_ABORT. If an exception of severity TP_REQUEST is generated within a server and is related to its servicing of a client, then the service is terminated with an error status.

TP_TRANSACTION
Aborts the associated transaction. For more information, refer to the xa_rollback command.

TP_CONNECTION
If associated with a client exception, the associated connection automatically closes. Refer to the client_exit command. If no connection applies, the severity is reduced to the next severity level that is applicable, usually TP_COMMAND.

If associated with a server exception, the server aborts its current service and shuts down.

TP_PANIC

Operation of the agent, client or server, stops. A client process rolls back all outstanding transactions (via xa_rollback), aborts if possible all outstanding service requests, closes all open connections, and terminates. A server aborts its current service as if the severity were TP_REQUEST. and shuts down. The connection to the middleware API is severed.

Built-in Handlers

Panther provides four exception handlers:


Jif_changed Events

jif_changed events occur when the jif_check command detects changes in the JIF since application startup or the last time the JIF was read. Each time the JIF is accessed, jif_check examines it for changes.

Jif_changed Handlers

A jif_changed handler should call the jif_read command to reread the JIF, then readvertise services through calls to unadvertise and advertise. If necessary, this handler can also unload public JPL modules that contain service definitions, then reissue the public command on them.

For example, the following handler rereads the JIF and readvertises all services:

proc jif_changed_hdlr()
unadvertise all
jif_read
advertise all
return 0

The jif_changed event handler can conditionally reread the JIF by checking the tp_return property, which tells how the JIF changed with one of these values:

For example, the following JPL rereads the JIF only if it the current version is more recent than the last:

if ( @app()->tp_return == TP_JIF_NEWER )
jif_read

Scope

The jif_changed event is available to clients and servers. A jif_changed handler can only be installed at application scope

Contract

jif_changed_handler()

Returns

A negative return code causes the jif_check command to abort by raising a TP_USER_ABORT exception of severity TP_COMMAND.

Built-in Handlers

Two built-in jif_changed handlers are provided:


Message Events

message events occur when a client receives an unsolicited message, including those generated by the broadcast, and notify commands, or when a subscribed event is posted with the post command.

Message Handlers

A message handler decides what to do with unsolicited messages received by Panther. Typically, an application logs messages to a file or displays them immediately to the user.

If a client needs to process or save a message, the message handler must do so before it returns. Use the receive command with the MESSAGE keyword to access message contents.

Recognizing the Message Source

To ensure that unsolicited messages are received and interpreted correctly, install an application-wide message handler that relies on a common protocol for identifying message sources. For example, the protocol might establish that all agents of unsolicited messages use the same field in their message data to identify the message source. On receiving unsolicited messages, the message handler then checks this field to determine the nature of the message and how to respond.

In the following example, an unsolicited message is broadcast to a supervisor client to report a security violation. In this application, the first field of an unsolicited message is always named source, whose value indicates the message type. The message handler first issues the receive command to get the contents of source; subsequent processing depends on the contents of source:

broadcast CLIENT "supervisor" TYPE JAMFLEX \
({source="bcast_security", ACCOUNT=acct, DATE=date,\
SECURITY=code, MSG=message})
// Message handler for all unsolicited messages
proc msg_handler(type, subtype)
vars source, account, date, security, message
vars companyNews, teamNews, stock, stock_quote
vars fileStream, acctMsg
// Identify message sender.
receive MESSAGE ({source})
if (source == "bcast_security")
{
// receive security violation data
receive MESSAGE ({account, date, security, message})
// Alert the supervisor
msg emsg "%A004Security alert: " ## message ## \
"%NDate: " ## date ## \
"%NAccount No. " ## account ## \
"%NCode: " ## code
}
else if (source == "bcast_acct_data")
{
// receive account data
receive MESSAGE ({account, date, message})
acctMsg = account##" "##date##" "##message
	// write message data to log file
fileStream = sm_fio_open("/u/acct/logfile", "a")
if fileStream > 0
{
call sm_fio_puts (acctMsg, fileStream)
call sm_fio_close(fileStream)
}
}

...

else if (source == "post_comp_news")
{
// receive posted company news message data
receive MESSAGE ({ companyNews })
msg emsg "Latest company news: " ## companyNews
}

...

return 0

Scope

message events are restricted to clients, and a message handler can only be installed at application scope.

Contract

message_handler(char *msgType, char *msgSubType)

msgType
Set to the message data type as specified in the broadcast or notify command, either JAMFLEX, FML, FML32, or STRING. If the application is likely to broadcast different message data types, the message handler should evaluate the contents of this parameter and branch execution accordingly.

msgSubType
Currently unused, reserved for future use.

Returns

Ignored; a 0 return value is suggested.

Built-in Handlers

Two message handlers are provided:


Pre_request and Post_request Events

pre_request and post_request events occur when a client or server issues a service request. A pre_request event occurs just before the actual service request is initiated. A post_request event occurs when the service returns, whether or not the service completes normally. Each service request generates a pre_request and post_request event.

pre_request and post_request events are typically used to track the number of service requests, and how long they take to return.

Pre_request and Post_request Handlers

A pre_request handler is responsible for aborting the service request if execution is inappropriate.

A non-negative (>=0) return code from a pre_request handler allows service request processing to continue. A negative return code aborts the request by generating a USER_ABORT exception of severity TP_REQUEST; a post_request event follows immediately follows.

Scope

pre_request and post_request events are available to clients and to servers acting as clients—that is, wherever service requests can occur (refer to service_call). pre_request and post_request handlers can only be installed at application scope.

Contract

pre_request_handler(char *callid, char *serviceName, HR>
   char *cmdStr)
post_request_handler(char *callid, char *serviceName, HR>
   char *cmdStr)

callid
The identifier that Panther assigned to the service request.

serviceName
The name of the invoked service.

cmdStr
The text of the service_call command and its arguments that issued the service request.

Returns

Ignored; a 0 return value is suggested.

Built-in Handlers

One default handler is provided for each event type:


Request_received Events

request_received events are generated on a server when it receives service requests. The request_received event occurs before Panther verifies that the service is defined in the JIF.

Request_received Handlers

The request_received handler performs any processing that is related to receipt of a service request. Because a request_received event occurs before Panther verifies a service definition, its handler typically calls jif_check to determine whether the JIF has changed.

A request_received handler typically works together with a jif_changed handler to ensure that all changes to the JIF are known to the server before it processes a service request. For example, if the request_received handler calls jif_check, this command detects any changes to the JIF and generates a jif_changed event. The jif_changed handler can then readvertise the services configured for this server (refer to page 6-16).

For example:

// Check whether JIF has changed. If it has, a jif_changed
// event is raised and jif_read executes

proc request_received_hdlr (callid, serviceName)
jif_check
return 0

request_received handlers abort a service request if it is inappropriate to proceed.

Scope

request_received events are executed only on servers. request_received handlers can only be installed at application scope.

Contract

request_received_handler(char *callid, char *serviceName)

callid
The identifier assigned to the service request.

serviceName
The name of the service as invoked by the client.

Returns

A non-negative return code from a request_received handler allows continued processing of the service request. A negative return value aborts the service request.

Built-in Handlers

Two request_received handlers are provided:


Server_exit Events

A server_exit event is generated when a server deactivates in an orderly fashion. It is usually the last event that user-written code handles before server deactivation.

Server_exit Handlers

The server_exit handler should clean up any resources allocated while the server was active that the middleware API nor Panther are unaware of. It can also log a message that the server is deactivating. For example:

// Implementation of a "log_down" server_exit handler

proc server_exit
log "Server has been brought down."
return 0

Scope

server_exit events are restricted to servers, and the handler can only be installed at application scope.

Contract

server_exit_handler()

Returns

Ignored; a zero return is recommended.

Built-in Handlers

Two server_exit handlers are provided:


Pre_service and Post_service Events

The pre_service and post_service events occur every time a server receives a service request:

pre_service and post_service events are paired events—each pre_service event is eventually matched by a post_service event for that same service. Both events are enabled or disabled together.

Pre_service and Post_service Handlers

The pre_service handler should abort the service if execution is inappropriate. pre_service and post_service handlers typically keep track of the number of requests to a service and how much time a service requires to process. These handlers also can associate a service component with the service: the pre_service handler opens the service component, while the post_service handler closes it.

A non-negative return code from a pre_service handler allows continued execution of the service. A negative return code aborts the service: a TP_USER_ABORT exception of severity TP_REQUEST is generated, and the middleware API informs the client that the service has been aborted with a failure status. A post_service event is then generated.

Scope

pre_service and post_service events are restricted to servers. pre_service and post_service event handlers can only be installed at application scope.

Contract

pre_service_handler(char *callid, char *serviceName,
char *serviceContainer, int cacheScreen)
post_service_handler(char *callid, char *serviceName,
char *serviceContainer, int cacheScreen)

callid
The identifier assigned to this service request.

serviceName
The name of the service as invoked by the client.

serviceContainer
The name of a service component associated with the service (from the JIF).

cacheScreen
Tells whether the service component is cached in memory and so determines how the handlers access serviceContainer, with one of these values:

Returns

Ignored.

Example

The following is an example of a pre_service handler:

proc pre_service_hdlr(callid, srvcName, srvcContainer, cacheFlag)

vars rcode, log_msg
{
// if there is no service component, do nothing
if (cacheFlag == TP_NOCACHE)
{
rcode = sm_jwindow(srvcContainer)
}
else
{
rcode = sm_n_wswlwct(srvcContainer)
if (rcode < 0)
{
// if select failed & ONFIRSTCALL, try to open
if (cacheFlag == TP_ONFIRSTCALL)
{
rcode = sm_jwindow("&&" ## srvcContainer)
}
}
}
if (rcode < 0)
{
log_msg = "Error opening " ## srvcContainer
log log_msg
return -1
}
return 0
}

The following is an example of a post_service handler:

proc post_service_hdlr(callid, srvcName, srvcContainer, cacheFlag)

//if there is no service component, do nothing
if (srvcContainer == ""
return 0
{
if (cacheFlag == TP_NOCACHE)
{
call sm_close_window()
}
else
{
// handle as TP_ONFIRSTCALL and/or TP_ONADVERTISE
call sm_wdeslect()
}
}

Built-in Handlers

Four pre_service handlers are provided:

Four post_service handlers are provided:


Unload Events

An unload event occurs when message data is received from an external source whose contents can be written to Panther variables. Message data can be unloaded for these reasons:

All service requests generate an unload event on the client even if an error occurs and no data unloads. The application can then decide whether to write to the specified variables, possibly only clearing them of old data.

Unload Handlers

An unload handler is responsible for unloading data into Panther variables through the unload_data command, and setting conditions for performing the unload.

Note: unload handlers can be triggered anywhere, including within another unload handler. Therefore, unload handlers should not raise another unload event, and thus give rise to an infinite loop condition. For instance, if a service call is issued from within an unload handler, an unload event occurs for that service when it returns. The handler is called again to handle the event, and so on.

Scope

unload events are available to both servers and clients, and handlers can be installed at application, transaction, and request scopes.

Contract

unload_handler(char *callid, int msgSource, int receiptMethod)

callid
Identifies the message's service call. If the message is not associated with a service request (for example, an unsolicited message), this parameter is empty.

msgSource
The source of the message whose receipt caused the unload event, indicated by one of the following constants:

msgSource constant Location Source

TP_BLOCKING_RPC

client/server

Normal blocking service_call

TP_NONBLOCKING_RPC

client/server

Nonblocking service_call

TP_ARGUMENTS

server

Arguments sent to the service

TP_UNSOLICITED

client

Unsolicited message

receiptMethod
Indicates the type of polling used to obtain the message with one of these constants:

receiptMethod constant Location Method

TP_ASYNCHRONOUS

client

Data received from asynchronous polling

TP_BLOCKING

client/server

Data received while blocking as part of service_call command

TP_SERVER

server

Message obtained through whatever mechanism is used by a server to receive service requests; only if msgSource is TP_ARGUMENTS

TP_WAIT

client/server

Data received as a result of invocation of wait

Returns

The severity code that an unload handler returns indicates whether it successfully processed the unload event. TP_NONE indicates success. Return any other severity code to indicate failure. A failed unload event generates a TP_UNLOAD_FAILED exception at the specified severity. If the return code is not a valid severity, a TP_UNLOAD_FAILED exception occurs at the TP_WARNING severity level. For a description of severity codes, refer to page 6-13.

Example

The following unload handler unloads message data only if the service request is successful:

proc unload_hdlr (callid, msgSource, msgRcptMethod)
{
if (@app()->tp_svc_outcome == 0)
{
// unload the arguments
unload_data
}
else
{
log "Unsuccessful return from service--no arguments \
unloaded"
}
return TP_NONE
}

Built-in Handlers

Two unload handlers are provided.