Oracle9i Application Server Wireless Edition Developer's Guide
Release 1.1

Part Number A86700-01

Library

Solution Area

Contents

Index

Go to previous page Go to next page

6
Using the Runtime API

This document describes the Wireless Edition Runtime API. Each section of this document presents a different topic. These sections include:

6.1 Overview

The Wireless Edition Runtime processes requests from Hypertext Transaction Protocol (HTTP) user agents and autonomous runtime agents, and invokes the services in the repository for these agents. It performs automatic session tracking and terminates the sessions when they expire after the maximum interval of inactivity or when the sessions are invalidated when the users log out from the Wireless Edition. During the request execution, the Runtime dispatches the authentication, authorization, device identification, location acquisition, data logging and other processing logics to the respective modules.

The Wireless Edition Runtime API provides a Java interface to examine the runtime execution status, monitor the runtime execution behavior, and augment the default execution semantics. The Runtime API consists of three Java packages:

  1. oracle.panama.rt provides the interfaces to the essential Runtime objects for status examination.

  2. oracle.panama.rt.event provides the interface to monitor the runtime execution sequence based on the Java event model.

  3. oracle.panama.rt.hook provides the interfaces for the essential Runtime customizable components and the default implementation for these interfaces.

These three packages are included in the panana.zip file. Make sure you have included panama.zip in your Java classpath when you compile your Java application or plug-in modules that depend on the Runtime API.

6.2 Runtime Core

The oracle.panama.rt package defines the core of the Runtime API. Adapters that conform to the Runtime API must implement the oracle.panama.adapter.RuntimeAdapter interface. The classes that implement the RuntimeAdapter interface can use the Request, Response, Session, and ServiceContext interfaces in the oracle.panama.rt package.

The following sections describe the interfaces and classes in this package. The interfaces are:

The class in this package is:

6.2.1 Request

A request object is used to invoke services. Generally, it defines which service to invoke and the particular parameters needed to invoke that service. It also defines the user, device, and other Runtime contexts.

A listener can subscribe to events from a request.

The following methods in the Request interface allow you to access, replace, add, or remove the parameters that are associated with the request object:

Object getAttribute(AttributeCategory category, String name)

Object setAttribute(AttributeCategory category, String name,
Object attribute)

The methods access the name and value of the attributes, which can be user parameters, system parameters, or the contexts for adapters, hooks, and listeners.

There are three categories of attributes:

The most important attribute category for Request is PARAMETERS, which contains the query parameters submitted by the user. For HTTP user agents, Runtime parses the URL query string to retrieve the parameters. The runtime agents or other internal clients can set these parameters programmatically. Since Runtime may cache and rewrite the URL for HTTP user agents, some of the parameters are maintained in the URL cache for the user. Runtime may have to parse both the query string from the HTTP request and the URL cache to build a complete list of query parameters.

The reference model, which is described in Section 6.5, "The Runtime Execution Reference Model", shows that each time a new request object is created, Runtime passes the request object to the ListenerRegistrationHook to let the hook register listeners.

Table 6-1 describes the names of the system-defined parameters which are part of the PARAMETERS AttributeCategory in Request. The left column in the table shows the Java constants that you can use to retrieve the value of the parameter from the request object.

The SimpleResults and AdapterResults can refer to the Runtime variables, which are composed from the names of the parameters in Table 6-1 by appending two underscore characters (__) before and after the parameter name. These Runtime variables in the SimpleResults and AdapterResults are "place holders" which are replaced by the values of the parameters during the post processing phase before the FinalResult is returned to the requester.

Table 6-1 System Defined Request Parameters

Java Program Constants Representing the Name of the Parameter in the Request Object  The Name of the Parameter in the Request Object  Description 

Request.USER_NAME 

"PAuserid" 

The name of the user signing on to the Wireless Edition. 

Request.PASSWORD 

"PApassword" 

The password for the user signing on to the Wireless Edition. 

Request.EFFECTIVE_USER_NAME 

"PAeffuserid" 

The name of the effective user. 

Request.SERVICE_OID 

"PAoid" 

The object id of the requested service. 

Request.SERVICE_PATH 

"PAservicepath" 

The path of the requested service in the repository. 

Request.SECTION 

"PAsection" 

The name of the result transformer in the Master Service. 

Request.SESSION_ID 

"PAsid" 

The session id for tracking user sessions. 

Request.REQUEST_LANDMARK 

"PArlmk" 

The landmark setting for the current request. 

Request.SESSION_LANDMARK 

"PAslmk" 

The landmark setting for the current session. 

Request.LOGOFF 

"PAlogoff" 

The request to log off and invalidate the session. 

Request.LOGIN 

"PAlogin" 

The login page request. 

Request.GET_PAGE 

"PAgetPage" 

Get the static page in the Master Service. 

Line 4 in the following code example shows how the value of the PArlmk parameter can be retrieved from the Request object. Line 5 shows a statement for setting the Request parameter.

Example:

1. public void invoke(ServiceContext sc) { 
2.    . 
3.    Request request = sc.getRequest(); 
4.    String value = request.getParameter(Request.REQUEST_LANDMARK); 
5.    request.setParameter(Request.SESSION_LANDMARK, "Redwood City"); 
6.    . 
7.} 

6.2.2 Response

This interface represents the Response objects in the Runtime. A listener can subscribe to events from a Response. The Response object is the execution result of the prior Request object.

6.2.3 Session

This interface represents the session objects in the Runtime. A valid session is established after a Wireless Edition user has successfully logged in. Any request (or service invocation) can only be executed in a valid session context. A session can either expire after the session exceeds the max interval of inactivity or get invalidated when the user requests an explicit log out. Developers can store the session-long information in the corresponding session object.

A listener can subscribe to events from a session.

The reference model, which is described in Section 6.5, "The Runtime Execution Reference Model", shows that each time a new session object is created, Runtime passes the session object to the ListenerRegistrationHook to let the hook register listeners.

6.2.4 ServiceContext

A ServiceContext provides the service request context for a valid and authorized request. Semantically, a new ServiceContext object is created for each validated request. The ServiceContext stores the input parameters, output parameters, AdapterResult (if returned by the Adapter), and SimpleResult (either returned by the Adapter or generated by the ResultTransformer). The associated request and session can be accessed by way of the ServiceContext object.

The AdapterResult and SimpleResult can refer to the system defined ServiceContext parameters using the Runtime variables as "place holders," which are substituted with values during the post processing phase before the FinalResult is returned to the requester. For more information on post processing, see the discussion regarding the PostProcessor in Section 6.5.1, "The Reference Model".

Runtime variables are composed from the names of the parameters in Table 6-1 and Table 6-2 by appending two underscores (__) before and after the parameter name.

Example:

All the input parameters, output parameters, AdapterResult, and SimpleResult in the ServiceContext are externalized as an XML document.

This XML document is the input document for the transformers. The XSL stylesheets for the transformers must be written against the DTD for the ServiceContext's XML document.

Table 6-2 describes the system-defined ServiceContext parameters which are found among the ServiceContext arguments. The left column in the table shows the Java program constants that represent the names of the parameters in the ServiceContext object.

Table 6-2 The System Defined ServiceContext Parameters

Java Program Constants Representing the Name of the Parameter in the ServiceContext  The Name of the Parameter in the ServiceContext Object  Description 

ServiceContext.DEVICE 

"_LOGICAL_DEVICE' 

The name of the device model. 

ServiceContext.REQUEST_NAME 

"_REQUEST_NAME" 

The URI of the servlet.

For example, if the URL is http://www.oracle.com/ptg/rm?

PAoid=100

Then the URI of the servlet is:

/ptg/rm 

ServiceContext.SESSION 

"_SESSION" 

The SessionId URL

For example, PAsid=ukAj6hH 

ServiceContext.INP_FIRST_SERVICE_URL 

"_SERVICE_URL" 

The URL of the requested service in the repository

For example, /users/smith/news 

ServiceContext.INP_FIRST_SERVICE_NAME 

"_SERVICE_NAME" 

The name of the requested service in the repository

For example, news 

ServiceContext.FIRST_ACCEPT_LANG 

"_FirstAcceptLanguage" 

The first language in the list of accepted languages. 

ServiceContext.USER 

"_User" 

The effective user, which may be the authenticated user. 

ServiceContext.USER_LANGUAGE 

"_UserLanguage" 

The user's preferred language; it should be one of the user agent's accepted languages. 

ServiceContext.LONGITUDE 

"_Longitude" 

The current longitude location. 

ServiceContext.LATITUDE 

"_Latitude" 

The current latitude location. 

ServiceContext.SCREEN_COLS 

"_ScreenColumns" 

The number of columns that are displayed. 

ServiceContext.SCREEN_ROWS 

"_ScreenRows" 

The number of rows that are displayed. 

ServiceContext.SCREEN_WIDTH 

"_ScreenWidth" 

The width of the display. 

ServiceContext.SCREEN_HEIGHT 

"_ScreenHeigth" 

The height of the display. 

ServiceContext.USER_AGENT 

"User-Agent" 

The type of the user agent that is obtained from the HTTP header. 

ServiceContext.ACCEPT_LANG 

"Accept-Language" 

The list of the languages that are accepted by the user agent. 

In the following code fragment example, line 5 shows that the Java program constants can be used to refer to the parameters. Line 6 shows that the name of the parameter can be spelled out (case sensitive). The parameter "Accept_encoding" is not one of the parameters in Table 6-1 or Table 6-2. Line 7 shows that the parameters from the request object are also available among the ServiceContext arguments. However, the ServiceContext parameters in Table 6-2 are not part of the PARAMETERS attribute category in Request objects, and they are not accessible from the Request objects.

Example:

1. public void invoke(ServiceContext sc) { 
2.    . 
3.    Arguments args = sc.getInputArguments(); 
4.    .
5.    String language = args.getInputValue(ServiceContext.USER_LANGUAGE);
6.    String encoding = args.getInputValue("Accept_encoding"); 
7.    String landmark = args.getInputValue(Request.REQUEST_LANDMARK); 
8.} 

The Java program constants in Table 6-3 below represent the names of the tags in the XML documents for the ServiceContext. The "ServiceRequest" tag is the root element of the ServiceContext. The "Result" tag contains the AdapterResult and SimpleResult. The "Arguments" tag is a sibling of the "Result" tag; it contains all input and output arguments.

Table 6-3 The XML Tag Names for ServiceContext and Results

Java Program Constants Representing the Names of the XML Tags in the ServiceContext  The Name of the XML Tag  Description 

ServiceContext.SERVICE_REQUEST 

"ServiceRequest" 

XML element containing service context 

ServiceContext.RESULT 

"Result" 

XML element containing adapter and simple results 

The following example of the XML document for a ServiceContext shows the "ServiceRequest" tag as the root element of the ServiceContext. Some of the input arguments in the following ServiceContext example are described in Table 6-1 and Table 6-2. Several of these input arguments (tags 21 to 28) are obtained from the HTTP header attributes.

Example of the XML document for a ServiceContext:

1. <ServiceRequest>
       a. <Arguments>
               i. <Inputs>
                    1. <PAsid type="SingleLine" 
usage="true">BVlcv</PAsid> 2. <PAoid type="SingleLine"
usage="true">244</PAoid> 3. <PAservlet type="SingleLine"
usage="true">rm</PAservlet> 4. <PAdebug type="SingleLine"
usage="true">1</PAdebug> 5. <_SERVICE_NAME type="SingleLine"
usage="true">Employee</_SERVICE_NAME> 6. <_SERVICE_NAME_ENC type="SingleLine"usage="true">
Employees</_SERVICE_NAME_ENC> 7. <_SERVICE_URL type="SingleLine" usage="true">
/home/Employees</_SERVICE_URL> 8. <_SERVICE_URL_ENC type="SingleLine" usage="true">
/home/Employees</_SERVICE_URL_ENC> 9. <_LOGICAL_DEVICE type="SingleLine" usage="true">HTML
</_LOGICAL_DEVICE> 10. <_SESSION type="SingleLine" usage="true">PAsid=BVlcv
</_SESSION> 11. <_REQUEST_NAME type="SingleLine" usage="true">/p2g/rm
</_REQUEST_NAME> 12. <_ScreenColumns type="SingleLine" usage="true">0
</_ScreenColumns> 13. <_ScreenRows type="SingleLine" usage="true">0
</_ScreenRows> 14. <_ScreenWidth type="SingleLine" usage="true">0
</_ScreenWidth> 15. <_ScreenHeigth type="SingleLine" usage="true">0
</_ScreenHeigth> 16. <_User type="SingleLine"
usage="true">user1</_User> 17. <_UserLanguage type="SingleLine"
usage="true"/> 18. <_FirstAcceptLanguage type="SingleLine" usage="true">ja
</_FirstAcceptLanguage> 19. <_Longitude type="SingleLine"
usage="true"/> 20. <_Latitude type="SingleLine"
usage="true"/> 21. <accept type="SingleLine" usage="true">image/gif,
image/x-xbitmap, image/jpeg,
image/pjpeg, image/png, */*</accept> 22. <accept-charset type="SingleLine" usage="true">
iso-8859-1,*,utf-8</accept-charset> 23. <accept-encoding type="SingleLine"
usage="true">gzip</accept-encoding> 24. <host type="SingleLine"
usage="true">localhost</host> 25. <cookie type="SingleLine" usage="true">
kurt=NTJCMUIzNzczQTA1QzBFRDAxNzY
3ODdBNEYxNTc0RkYwMDc1Rjc1MjFFU29ubnk=</cookie> 26. <accept-language type="SingleLine"
usage="true">ja,en</accept-language> 27. <connection type="SingleLine"
usage="true">Keep-Alive</connection> 28. <user-agent type="SingleLine"
usage="true">Mozilla/4.5
[en] (WinNT; U)</user-agent> ii. </Inputs> b. </Arguments> c. <Result> i. <AdapterResult> 1. <Table> a. <Row> i. <EMPNO>10</EMPNO> ii. <FNAME>Scott</FNAME> b. </Row> c. <Row> i. <EMPNO>20</EMPNO> ii. <FNAME>Tiger</FNAME> d. </Row> 2. </Table> ii. </AdapterResult> iii. <SimpleResult> 1. <SimpleContainer name="Services"> a. <SimpleMenu name="alias" title="Employees"> b. <SimpleMenuItem
target="/p2g/rm?PAsid=BVlcv&#38;
PAckey=6!">Scott</SimpleMenuItem> c. <SimpleMenuItem
target="/p2g/rm?PAsid=BVlcv&#38;
PAckey=7!">Tiger</SimpleMenuItem> d. </SimpleMenu> 2. </SimpleContainer> iv. </SimpleResult> d. </Result> 2.</ServiceRequest>

6.2.5 ManagedContext

In many situations, the customized hooks, listeners, and adapters require session-long, user-defined context information to be stored in the session object, so that subsequent calls or requests can access the context information. Furthermore, these application contexts may contain system resources that should be freed when the session is closed.

The user-defined context must implement the ManagedContext interface and provide customized implementation for the invalidate method. The customized hooks, listeners, and adapters can register the session-long application context object with the session through the setManagedContext method. The invalidate method will be called by Runtime when the session terminates.

6.2.6 RequestFactory

The RequestFactory class is defined in the oracle.panama.rt package. The RequestFactory provides the APIs to programmatically create request objects to be executed. The RequestFactory creates the request objects that, when executed, initiate the runtime controllers to process the service requests by invoking the necessary business processes, such as session management, authentication, authorization, service invocation, and result transformation.

Case 1: Application of the RequestFactory Pattern in the HTTP Servlet

This case uses the ParmImpl servlet to illustrate the RequestFactory pattern. The following code example is the doGet() method of the ParmImpl servlet:

1. public void doGet(HttpServletRequest request, HttpServletResponse response)
2. throws IOException, ServletException { 3. Request req = RequestFactory.createRequest(request, response); 4. if (req == null) { 5. return; 6. } 7. try { 8. Response resp = req.execute(); 9. } catch (Exception ex) { 10. } finally { 11. req.invalidate(); 12. } 13.}

Line 3 in the above example illustrates the use of the static method createRequest(HttpServletRequest request, HttpServletResponse response) of RequestFactory to create a Request object.

When the Request object is executed in line 8, it returns a Response object.

The Java code in the above example does not include reading or writing of the content in the Response object because the runtime controller directly transfers the content to the HttpServletResponse object.

The execute()method of the Request object starts a control flow which performs the following sequence of processes:

  1. Assign a session to the request.

  2. Parse the URL parameters in the HttpServletRequest.

  3. Authenticate the user if the user credentials are provided among the parameters.

  4. Authorize the requested service.

  5. Invoke the service.

  6. Transform the XML result from the service invocation.

  7. Convert the final XML result to a string.

  8. Set the response string in the HttpServletResponse.

  9. Return to the servlet.

Line 11 in the code example invalidates the completed object, thereby freeing all the resources associated with the request object.

Case 2: Application of the RequestFactory Pattern in the Runtime Agent

The following case illustrates how a runtime agent uses the RequestFactory pattern to request services from the Wireless Edition Runtime.

1.import oracle.panama.rt.RequestFactory;
2.import oracle.panama.rt.Request;
3.import oracle.panama.rt.Response;
4.import oracle.panama.rt.Session;
5.import oracle.panama.rt.ServiceContext;
6..
7.import oracle.panama.model.MetaLocator;
8.import oracle.panama.model.ModelServices;
9.import oracle.panama.model.Service;
10.import oracle.panama.model.User;
11.import oracle.panama.model.AlertAddress;
12..
13..
14..
 
15.Session signon(String user, String password) throws PanamaException {
16.     Request request = RequestFactory.createRequest(user, password);
17.     request.validate();
18.     Session session = request.getSession();
19.     request.invalidate();
20.     return session;
21.}
 
22.String invokeService(Session session, Service service, User user,
AlertAddress address, String symbol) { 23. Request req; 24. Response resp; 25. ServiceContext sc; 26. String content = null; 27. try { 28. req = RequestFactory.createRequest(session, service,
user, address); 29. if (req == null) { 30. return null; 31. } 32. try { 33. req.setParameter("TickerSymbol", symbol); 34. sc = req.validate(); 35. resp = req.execute(); 36. if (sc.isAnyResultPresent()) { 37. content = resp.getContent(); 38. } 39. } catch (Exception ex) { 40. } finally { 41. req.invalidate(); 42. } 43. return content; 44. } 45. } 46.String userName; 47.String password; 48.String effectiveUserName; 49.String symbols[]; 50. 51.void main() { 52. ModelServices models = MetaLocator.getInstance().getModelServices(); 53. User user = models.lookupUser(effectiveUserName); 54. Service service = models.lookupService("YahooQuote"); 55. AlertAddress[] addresses = user.getAddresses(); 56. Session session = signon(userName, password); 57. for (int i = 0; i < symbols.length(); i++) { 58. . 59. . 60. String content = invokeService(session, service, user, addresses[0],
symbol[i]); 61. . 62. . 63. } 64. }

The signon() method signs on the user to the Runtime. When the Request object is validated in line 17, the user name and password credentials are used to authenticate the user. Since no service is invoked during the sign-on request, the code example shows that the Request object is not executed.

If there is no exception after validation, the authenticated session is retrieved from the Request object in line 18.

The Session object is used in the invokeService() method for subsequent requests to the Runtime. Line 28 in the invokeService() method creates a Request object for an effective user and a specified service. For this operation to succeed, the authenticated user must have administrative privileges over the effective user account.

The address parameter identifies the target device model for the Runtime to format the content in the appropriate markup language.

The main routine in the above code example illustrates how it iteratively invokes the service each time with a different input parameter. The contents returned by each service request can be combined into a larger document and sent to the user.

6.3 Event, Listener

During the establishing of a session, the expiration of a session, or the processing of a request, Runtime can generate a sequence of events to signal the execution progress if any interested listener is registered with these objects. Generally, listeners should not be intrusive to the Runtime. They should monitor the Runtime progress instead of altering its execution behavior. The possible applications for the event package can be a logger, a billing procedure, or a performance monitor tool. The oracle.panama.rt.event package defines the Listener and Event API.

Listeners listen to Events. Listener and Event form an important design pattern in which the Listener is an observer. Three types of listeners are defined:

The registration of a listener by the ListenerRegistrationHook conforms to the publish-subscribe model. The ListenerRegistrationHook subscribes the listeners to receive events from the subject, such as Request, Response, or Session.

6.3.1 Implementing the RequestListener Interface

The implementation of oracle.panama.rt.event.RequestListener can receive the Request-related event.

The possible Request-related events can include any of the following events:

The timing sequence regarding when the event is generated is discussed in Section 6.5, "The Runtime Execution Reference Model". However, not all the Request-related events will be generated. Which specific Request-related event will be generated is controlled by the event mask in the System.properties file. Note that the event mask is system-wide.

For example, if you want to generate the request begin event and have your RequestListener receive the event, you should set the request.begin mask to true in the System.properties file as shown below:

Event.request.begin=true.

The reference model, which is described in Section 6.5, "The Runtime Execution Reference Model", indicates that the RequestListener can intercept the input parameters during the requestBeginRequest(Event)(Step 11 in Section 6.5.1, "The Reference Model") and apply additional business rules to the request parameters before service invocation.

6.3.2 Implementing the ResponseListener Interface

The implementation of oracle.panama.rt.event.ResponseListener can receive the Response-related event. The only possible Response-related event is response error. The event constant is defined in the oracle.panama.rt.event.ResponseEvent interface. If you want Runtime to generate the response error event when the response has an error and have your ResponseListener receive the event, you should set the response error mask to true in the System.properties file as shown below:

Event.response.error=true

6.3.3 Implementing the SessionListener Interface

The implementation of oracle.panama.rt.event.SessionListener can receive the Session-related event. The possible Session-related events include:

The timing sequence regarding when the event is generated is discussed in Section 6.5, "The Runtime Execution Reference Model". However not all the Session-related events will be generated. Which specific Session-related event will be generated is controlled by the event mask in the System.properties file. Note that the event mask is system wide.

For example, if you want to generate the session begin event and have your SessionListener receive the event, you should set the session begin mask to true in the System.properties file as shown below:

Event.session.begin=true

6.3.4 Guidelines

The following guidelines describe how to set up the customized Event Listener:

  1. Implement the RequestListener, ResponseListener, or SessionListener interface.

  2. Implement the ListenerRegistrationHook interface.

  3. Compile the new Java source files from Steps 1 and 2 with the panama.zip file in the classpath.

  4. Modify the event mask entries in the System.properties file to enable the generation of specific events.

  5. Modify the entries to the ListenerRegistrationHook.

  6. Modify the class.wrapper in the JServ.properties file to include the classpath for the new Java classes.

  7. Restart JServ (or the Oracle HTTP Server).

Any of the event listeners may raise the AbortServiceException to signal the Runtime controller to reject the request, but this veto signal is effective only if it is raised during one of the following events when the service is yet to be invoked:

The listeners may raise the AbortServiceException during the serviceEnd(), transformBegin(), and transformEnd() events to refuse the service's content to the user, although any durable effect of the service invocation cannot be rolled back.

The sessionEnd(), afterSession(), requestEnd(), and afterRequest() methods should not raise the AbortServiceException.

A listener that implements the Request, Response, and Session listener interfaces is described in the code example in Section 6.8.2, "Event Listener Example". The listener in this example listens to all Request, Response, and Session events. This listener logs the response time, service time, and transform time of the requests.

The values placed in the event object persist through the life cycle of the event source and can be retrieved during subsequent events. Alternatively, the listener may place the values in the RUNTIME attribute category of the Request or Session objects. Both techniques allow the listeners to correlate and trace the events from individual event sources.

6.4 Hooks

The Runtime specifies the hook interfaces for standard plug-in modules. The following sections describe the hooks in the order in which they are applied by the Runtime. See the reference model in Section 6.5, "The Runtime Execution Reference Model".

In the Wireless Edition Runtime API, Hook and Policy form a design pattern, within which Policy is the default implementation of Hook.

The following table lists the Hooks and the default Policies that correspond to the hook interfaces:

Table 6-4 Classes that Implement the Default Policies

Hook Name  Policy Name 

AuthenticationHook 

AuthenticationPolicy 

AuthorizationHook 

AuthorizationPolicy 

CallerLocationHook 

CallerLocationPolicy 

DeviceIdentificationHook 

DeviceIdentificationPolicy 

FolderRendererHook 

FolderRendererPolicy 

HomeFolderSorterHook 

HomeFolderSorterPolicy 

ListenerRegistrationHook 

ListenerRegistrationPolicy 

LocationServiceVisibilityHook 

LocationServiceVisibilityPolicy 

PostProcessorHook 

 

PreProcessorHook 

 

ServiceVisibilityHook 

ServiceVisibilityPolicy 

SessionIdHook 

SessionIdPolicy 

SignOnPagesHook 

SignOnPagesPolicy 

SubscriberIdHook 

 

SystemPasswordEncryptionHook 

 

6.4.1 The ListenerRegistrationHook

The reference model, which is described in Section 6.5, "The Runtime Execution Reference Model", shows that each time a new Session or Request object is created, Runtime passes the Session or Request object to the ListenerRegistrationHook to let the hook register listeners. The listener registration module can be customized to let the listeners selectively observe the event sources.

For example, a custom listener registration policy may subscribe the listeners only to the requests for the Web Integration Adapter services. Such a listener may add business rules to the Runtime controller.

6.4.2 The DeviceIdentificationHook

Runtime uses the DeviceIdentificationHook to determine the device model for the user agent. For HTTP clients, the user-agent type is the value of the "User-Agent" attribute in the HTTP header. The DeviceIdentificationHook can implement robust determination of the type of user agents for cases where the user-agent attribute is not supplied in the HTTP header.

This hook provides a mapping of the user-agent type to the device model. Runtime agents can specify the Device in the RequestFactory method. If the Device is specified, the Runtime controller will not invoke the DeviceIdentificationHook.

Although customization and extensions are supported, the default device identification policy is fully functional.

6.4.3 The SessionIDHook

The Wireless Edition Runtime uses the SessionIdHook to uniquely identify each new session it creates with a Session id. This Session id is used in the URLs for session tracking. It is important for custom Session id modules to generate long Session id strings. Longer Session id strings are less vulnerable to attack.

6.4.4 The AuthenticationHook

The Wireless Edition Runtime dispatches the authentication operations to the authentication module that implements the AuthenticationHook. The AuthenticationPolicy provides a public interface to the default authentication policy in Runtime. The default policy uses the user name and password credentials located in the Wireless Edition repository.

A different implementation of the AuthenticationHook using an external module may use any custom authentication scheme to validate the user. The external authentication module may optionally fail over to the default authentication policy.

The AuthenticationHook returns the AuthenticationContext if the authentication succeeds. Otherwise, the hook raises the AuthenticationException. The AuthenticationContext that is returned by the authentication module specifies the User object for the Session. This User object may be located in the Wireless Edition repository or provisioned by the authentication module on demand.

The AuthenticationContext is passed to the AuthorizationHook for service authorization. The String getAuthenticationType() method in Request can provide the name of the authentication scheme used by the plug-in authentication module, which extends the "BASIC", "DIGEST", or "SSL" authentication schemes supported by the javax.servlet.http package.

Runtime provides infrastructure support to mix and match different authentication, authorization, and provisioning policies by delegating the authentication operation to the AuthenticationHook and the authorization operation to the AuthorizationHook.

Runtime places the AuthenticationContext, which is returned by the AuthenticationHook, in the Session. The AuthenticationContext is passed only to the AuthorizationHook and is not accessible through the public interface.

The AuthenticationHook may either create the user or look up the user in the repository. If the user is provisioned by the external accounting system, the AuthenticationHook will also provision the home folder and group for the user. The user, which is returned through the AuthenticationContext, becomes the authenticated user of the session. Although large-scale customization and extension efforts are supported, the built-in authentication and authorization policies are fully functional.

6.4.5 The SignOnPagesHook

The SignOnPagesHook generates the sign-on pages for Runtime. This hook is not shown in the execution reference model because it is invoked only when the AuthenticationPolicy raises the AuthenticationFailOverException to request the sign-on pages.

When the SignOnPagesHook generates the sign-on page, the Wireless Edition Runtime sends that sign-on page to the user, who submits the user's name and password for authentication by the AuthenticationPolicy. The sign-on page is returned to the user agent only after the authentication module fails to authenticate the user by using any combination of credentials besides the user name and password.

The default policy for the SignOnPagesHook obtains the sign-on page from the device model in the repository. The default policy is fully functional without customization. The device model is identified and provided to the SignOnPagesHook. This hook returns the sign-on page formatted in the markup language for the target user agent. The hook may use the XML transformers of the device to render the sign-on page as long as the input to the transformer conforms to the SimpleResult DTD.

6.4.6 The SubscriberIDHook

Runtime invokes the SubscriberIdHook to determine the subscriber's ID.

The SubscriberId may be supplied by the external accounting system, by one of the fields (such as, the external ID field) of the user object in the repository, or by one of the attributes in the HTTP header. This SubscriberId is associated with the authenticated session.

6.4.7 The AuthorizationHook

The authentication operation is performed only one time to establish a session for the user. The authorization operation is performed for each request to the Wireless Edition Runtime.

The authorization module that implements the AuthorizationHook may use any custom authorization scheme. It is probable for the same party to implement both the AuthenticationHook and the AuthorizationHook. For example, in an environment that uses a pre-billing scheme, the AuthenticationHook provides the AuthenticationContext that indicates the user's prepaid level or type of service to the AuthorizationHook.

The external authorization module may optionally fail over to the default authorization policy by delegating to the AuthorizationPolicy provided in the public package. The default authorization policy authorizes the service using the visibility, validity, ownership, and group membership configuration in the repository.

6.4.8 The CallerLocationHook

The CallerLocationHook provides the interface to acquire a caller's physical location in terms of latitude and longitude. The Wireless Edition provides two different default implementations of the CallerLocationHook interface.

The oracle.panama.rt.common.CallerLocator class provides the simple implementation using the locationmarks. The landmark object is one way of specifying the longitude and latitude position. The user can change the landmark setting in the session through the URL parameter PAslmk. If the automatic location acquisition is disabled, the landmark setting in the session supplies the current position of the mobile device to the location-based services.

The oracle.panama.rt.hook.LocAcq provides the automatic location acquisition implementation if the user selects the Auto-Locate option. If the automatic acquisition fails or if the user selects the manual position option, then the prior locationmark semantics will be applied.

See the oracle.panama.mp section for details on how to specify which mobile position server (either Ericsson or SignalSoft, or another customized server) is used to acquire the caller's location.

6.4.9 Service

Services are Wireless Edition repository objects. A Master Service object contains a RuntimeAdapter, which is chief among plug-in components. Folders are a type of service used for organizing other folders and services in the repository. The following three hooks control how the content of a folder gets rendered:

The FolderRendererHook uses the HomeFolderSorterHook and LocationServiceVisibilityHook to render the contents of the folder. When the user first signs on to the system, Runtime invokes the user's home folder. The built-in FolderRendererHook combines the contents of the home folder with the folders and services from one or more of the user's groups. The HomeFolderSorterHook is useful for sorting the group elements among the user's own elements. The LocationServiceVisibilityHook selects from the location-based subfolders in the folder for those whose regions intersect with the current position of the mobile device.

6.4.10 The PreProcessorHook, Transformer, and PostPorcessorHook

If the PreProcessorHook is specified, the Runtime invokes the PreProcessorHook to process the SimpleResult from the service invocation. The DeviceTransformer is applied to the result of the PreProcessorHook. If specified, the PostProcessorHook is invoked to process the markup page that is generated by the DeviceTransformer.

6.5 The Runtime Execution Reference Model

The following sections discuss the request factory pattern and the Runtime execution reference model for it.

6.5.1 The Reference Model

This section describes the Wireless Edition Runtime reference model, showing how the hooks and listeners participate in the processing of a service request -- in this case, the request involves authentication and session establishment.

The sequence in the model shows how a service in the repository is invoked after authentication. If no service is specified in the request, as is the case for sign-on pages, the service which is invoked is that of the user's home folder.

6.5.1.1 Case: A Request Involving Session Establishment and Authentication

This is a description of the flow in how Runtime processes the events in a request that needs a new session and authentication. The numbers indicate the sequence of the actions in the Runtime.

  1. createRequest(HttpServletRequest,HttpServletResponse)

    ParmImpl submits an HTTP request containing input parameters to the RequestFactory to create the Request object.

  2. registerRequestListeners(Request)

    Runtime passes the object to the ListenerRegistrationHook to let it register listeners.

  3. beforeRequest(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  4. execute()

    ParmImpl executes the newly created Request object, which indicates to Runtime that a sequence of activities will follow.

  5. findDeviceType(String)

    Runtime dispatches to the DeviceIdentificationHook to determine the device model.

  6. createSessionId()

    Runtime dispatches to the SessionIdHook to create a new session id for the PAsid parameter.

  7. createSession()

    Runtime creates a new Session for the given session id.

  8. registerSessionListeners(Request,Session)

    Runtime passes the new Session to the ListenerRegistrationHook to let it register the session listeners.

  9. beforeSession(SessionEvent)

    The event source Session issues a notification to each of the SessionListeners, passing the SessionEvent object.

  10. parseInputParameters()

    Runtime parses the URL in the HTTP request and extracts the input parameters.

  11. requestBegin(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  12. authenticate(String,String,Request)

    Runtime dispatches to the AuthenticationHook to authenticate the user.

  13. getSubscriberId(Request,Session)

    Runtime dispatches to the SubscriberIdHook to obtain the subscriber id of the user, which can be used by the CallerLocationHook.

  14. sessionBegin(SessionEvent)

    The event source Session issues a notification to each of the SessionListeners, passing the SessionEvent object.

  15. getCurrentLocation(Request)

    Runtime dispatches to the CallerLocationHook to determine the location of the caller (mobile device).

  16. authorize(User,Service,Request,AutheticationContext)

    Runtime dispatches to the AuthorizationHook to authorize the requested service.

  17. serviceBegin(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  18. invoke(ServiceContext)

    Runtime invokes the service in the repository, passing the ServiceContext object.

  19. serviceEnd(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  20. transformBegin(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  21. process(Request,Element)

    Runtime dispatches to the PreProcessorHook to process the SimpleResult output of the service.

  22. rewriteResultURLs(Element)

    Runtime replaces the original URL with an encoded URL that contains the PAsid and PAckey parameters for the session id and the URL cache key, respectively.

  23. transform(Element,LogicalDevice)

    Runtime invokes the device ResultTransformer to transform the SimpleResult to the device's markup language.

  24. process(String,Arguments,Device)

    Runtime invokes the PostProcessor to parse the content of the device markup page. The PostProcessor replaces the Runtime variables (which are "place holders") with the values of the variables. For example, "PAsid=xyzw" replaces ___SESSION__.

    For more information about Runtime variables, see Section 6.2.4, "ServiceContext" and Appendix B, "Runtime System Variables".

  25. process(Request,Response,String)

    Runtime dispatches to the PostProcessorHook to process the device markup page to produce the FinalResult.

  26. transformEnd(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners, passing the RequestEvent object.

  27. writeContent()

    Runtime writes the content to the HTTPServletResponse.

  28. requestEnd(RequestEvent)

    The event source Request issues a notification to each of the RequestListeners.

  29. invalidate()

    ParmImpl invalidates the Request object.

  30. afterRequest(RequestEvent)

    The event source Request issues a final notification to the RequestListeners, passing the RequestEvent object.

6.6 System Parameters

There are two different kinds of system parameters: static and derived parameters. The following sections discuss these two types of system parameters.

6.6.1 Static System Parameters

The static system parameters that are listed in Table 6-1 and Table 6-2 are usually presented in the valid ServiceContext. You can access them in one of two ways:

The HTTP headers sent together with the HTTP service request invocation are also considered static parameters. However which HTTP header is present depends on the browser and on the gateway. To find out which HTTP headers are present in a request, use the following:

Enumeration in_http_headers = Req.getHeaderAttributes() 

This returns an enumeration of present HTTP headers in the request.

You can retrieve the HTTP header's value by enumerating the enumeration:

 while (in_http_headers.hasMoreElements())
 {
    String arg = (String) in_http_headers.nextElement();
    System.out.println(arg+"= "+ Reg.getParameter(arg));
}

6.6.2 Derived System Parameters

The second kind of system parameters is the derived parameters. A derived parameter's value is usually not present. To make its value present in the valid request object, do the following:

Add the derived parameter X to the master service and make the derived parameter X mandatory.

After each request has been validated, the Runtime computes the values for the mandatory derived parameters. Then the values of these derived parameters can be accessed in the same way as the values of the static system parameters. The Runtime-defined derived system parameters are listed in the following table:

Table 6-5 Derived System Parameters

Derived System Parameter Name  Description 

_Longitude 

The longitude component of the geocoding of the current requester's location 

_Latitude 

The latitude component of the geocoding of the current requester's location 

_State 

The state from which the current requester is initiating the request 

_Postalcode 

The postal code of the current requester's location 

_Country 

The country in which the current requester is initiating the request 

6.7 General Guidelines on User-Defined Listeners and Hook Implementation

Component developers can develop new types of runtime agents and adapters by using only the classes and interfaces in the public packages provided in the panama.zip file. The panama_core.zip file contains the internal packages that should only be used by built-in components.

The following steps describe how you provide your own implementation of listeners and hooks.

6.7.1 Implementing the Respective Interface

The user-defined listeners and hooks should implement the respective listener interface or the hook interface. For example, if you define your own AuthenticationHook, your new AuthenticationHook Java class should implement the oracle.panama.rt.hook.AuthenticationHook interface.

Furthermore, the new implementation should implement the following Singleton pattern:

class yourClass  implement Xhook  {
     public static Xhook getInstance() {   .... }
...
}

6.7.2 Compile Your Java Source

Make sure you have included the panama.zip in your Java classpath during compilation.

6.7.3 Plug in Your Implementation through Property File

Set the corresponding entry in the System.properties file to specify the name of the class that provides the implementation.

The following table lists the property entry name in the System.properties file for each hook.

Table 6-6 Property Entry Names in the System.properties File

Hook Name  Property Name 

AuthenticationHook 

locator.authentication.hook.class 

AuthorizationHook 

locator.authorization.hook.class 

CallerLocationHook 

locator.caller.location.hook.class 

DeviceIdentificationHook 

locator.device.identification.hook.class 

FolderRendererHook 

locator.folder.renderer.hook.class 

HomeFolderSorterHook 

locator.home.folder.sorter.hook.class 

ListenerRegistrationHook 

locator.listener.registration.hook.class 

LocationServiceVisibilityHook 

locator.location.service.visibility.hook.class 

PostProcessorHook 

locator.post.processor.hook.class 

PreProcessorHook 

locator.pre.processor.hook.class 

ServiceVisibilityHook 

locator.service.visibility.hook.class 

SessionIdHook 

locator.session.id.hook.class 

SignOnPagesHook 

locator.signon.pages.hook.class 

SubscriberIdHook 

locator.subscriber.id.hook.class 

SystemPasswordEncryptionHook 

locator.SystemPasswordEncryptionHook.class 

For example, if you provide your own implementation of the authentication hook, you should set the locator.authentication.hook.class in the System.properties file to:

locator.authentication.hook.class = <your class name>

6.7.4 Modify the JServ Property File

Add a new entry of wrapper.classpath in the Jserv.properties file to include the new path in which the new Java class files reside.

6.7.5 Restart the Server

You have to restart either the Apache Server if the JServ is launched automatically by the Apache Server or the JServ if the JServ is started manually. Then, your new implementation takes effect.

6.7.6 Tips and Hints

When implementing the new listeners and hooks, consider also the following points:

6.7.6.1 Concurrent Requests

The Runtime supports concurrent instances of requests from user agents through an HTTP connection. Concurrent requests are not permitted for the runtime agent that shares the same administrator session among different effective users. For this type of agent, the Runtime serializes the requests under the same session. Concurrency is achieved by introducing more than one instance of the runtime agents, each with its own authenticated session.

6.7.6.2 Recursive Instances of Requests

The Runtime supports recursive instances of requests under the same session. Recursive instances of requests may be issued by the plug-in components, for example, to recursively invoke all services under a folder.

6.7.6.3 Query Parameters

The Runtime parses the URL query strings from HTTP user agents to retrieve query parameters. For other agents that do not use URL strings, the Runtime lets the agents set the query parameters programmatically. The Runtime allows the agents to specify the session, user, device, and service using objects instead of names.

6.7.6.4 Runtime Object References

This design constraint requires that plug-in components do not retain references to the Runtime objects across invocations.

Plug-in components may execute under asynchronous threads; in this case, the synchronous methods in the components should make snapshots of the Runtime objects before handing them to the asynchronous threads.

6.7.6.5 Thread-Safe and High-Concurrency

Since a single instance of the customized listeners and hooks is created according to the Singleton design pattern, the Java class should provide a thread-safe but very high concurrent implementation. Otherwise, the performance of the Wireless Edition Runtime can be significantly degraded.

6.8 Examples

The following examples are available in the respective subdirectories under PANAMA_HOME\sample.

6.8.1 User-Defined Hooks

The following two examples illustrate how you can develop user-defined hooks:

6.8.1.1 Example 1

This example implements a user-defined FolderRendererHook. By examining the ptg_service_log table, the MyFolderRenderer displays the content of any folder for the current user based on the number of clicks the user has made in the past.

Note especially the High-Concurrency design in this example. Also, note the use of the ManagedContext interface.

The MyFolderRenderer.java Source Code:

/*
 * 
The MyFolderRenderer displays the content of any folder for the current
user based on the number of clicks the user has made in the past
by examining the ptg_service_log table.
*
To use:
1. configure the System.properties to enable the DB-based System logger.
2. Compile the MyFolderRenderer.java and MyFolderRendererContext.java.
Make sure you have included the panama.zip, panama_core.zip
and xmlparserv2.jar in the classpath.
3. Modify the jserv.properties to include the class files
in the wrapper.classpath.
*
Area of Improvement:
1. Instead of examining the ptg_server_log table, examine
another clickstream summary table whose data is computed offline
once aday.
2. Instead of dedicating a separate database connection for each
session for retriving the folder content's click stream summary,
use a database connection pool to avoid exhausting the db
connection resource.
*
*
*/
package hook;

import oracle.panama.rt.hook.FolderRendererHook;
import oracle.panama.rt.ServiceContext;
import oracle.panama.rt.ManagedContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import oracle.panama.model.Service;
import oracle.panama.model.ModelServices;
import oracle.panama.model.MetaLocator;
import oracle.panama.model.User;
import oracle.panama.model.Folder;
import oracle.panama.PAPrimitive;
import oracle.panama.core.util.Locator;
import oracle.panama.core.admin.L;

public class MyFolderRenderer implements FolderRendererHook {   [51]

private static MyFolderRenderer myFolderRenderer;

private MyFolderRenderer() { }

public static FolderRendererHook getInstance()  {               [55]
    // implements the Singleton patten
    if (myFolderRenderer == null)  {
       synchronized (MyFolderRenderer.class) {
          if (myFolderRenderer == null) {
             myFolderRenderer = new MyFolderRenderer();
          }
       }
       return myFolderRenderer;
    }                                                          [57]
}

// implement the invoke specified in the FolderRendererHook interface
public Element invoke(Folder folder, ServiceContext context) {
    // check whether the "MyFolderRendereredContext" has been registered  [60]
    // with the current session
    Session session = context.getSession();
    MyFolderRendererContext renderContext = (MyFolderRendererContext)
session.getManagedContext("MyFolderRendererContext"); if (renderContext == null) { synchronized (this) { if (renderContext == null) { // if not, create the RendererContext and register it // with the current seession renderContext = new MyFolderRendererContext(); context.getSession().setManagedContext("MyFolderRendererContext",
renderContext); } } } // retrieve the new displaying order for the folder's content [63] Service[] newOrder = renderContext.getNewOrder(folder,
context.getSession().getUser()); Document owner = context.getXMLDocument(); Element simpleResult = PAPrimitive.createSimpleResult(owner, null); Element simpleContainer = PAPrimitive.createSimpleContainer(owner,
Services"); simpleResult.appendChild(simpleContainer); if (newOrder != null && newOrder.length >0) { // construct the SimpleResult XML to display folder's content Element simpleMenu = PAPrimitive.createSimpleMenu(owner, "folder",
folder.getName()); for (int i=0; i < newOrder.length; i++) { String link = newOrder[i].getURLPathParameter(); Element menuItem = PAPrimitive.createSimpleMenuItem(owner,
newOrder[i].getName(), link, false); simpleMenu.appendChild(menuItem); } simpleContainer.appendChild(simpleMenu); } else { // display a message if the folder's content is empty Element simpleText = PAPrimitive.createSimpleText(owner); Element error = PAPrimitive.createSimpleTextItem(owner,
"Error", "No service items found"); simpleText.appendChild(error); simpleContainer.appendChild(simpleText); } return simpleResult; } }

In line 51, MyFolderRenderer class implements the oracle.panama.rt.hook.FolderRendererHook interface.

Between lines 55 and 57, the class implements the Singleton pattern.

As the class needs to connect to the database to examine the ptg_service_log table, it binds the session-long customized ManagedContext to the Wireless Edition session object in the code between lines 60 and 63.

This class is stateless as it does not have any instance variables. All the state information is stored in the session object as discussed earlier in this document to achieve thread-safe and high-concurrency objectives.

The MyFolderRendererContext.java Source Code:

package hook;

import oracle.panama.rt.hook.FolderRendererHook;
import oracle.panama.rt.ServiceContext;
import oracle.panama.rt.ManagedContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import oracle.panama.model.Service;
import oracle.panama.model.ModelServices;
import oracle.panama.model.MetaLocator;
import oracle.panama.model.User;
import oracle.panama.model.Folder;
import oracle.panama.model.Group;
import oracle.panama.PAPrimitive;
import oracle.panama.core.util.Locator;
import oracle.panama.core.admin.L;

public class  MyFolderRendererContext implements ManagedContext {

Connection conn ; 

public MyFolderRendererContext() {
    try {
        // lookup the db.connect.string in the panama's System.properties file
        String connectString = Locator.getInstance().getResource()
.getSystem().getString("db.connect.string", ""); // constrct the JDBC connect string, always use the THIN driver for // simplicity int i = connectString.indexOf('/'); String user = connectString.substring(0, i); int j = connectString.indexOf('@', i+1); String password = connectString.substring(i+1, j); String dbname = connectString.substring(j+1); StringBuffer connStrBuf = new StringBuffer("jdbc:oracle:thin:"); connStrBuf.append("@"); connStrBuf.append(dbname); // load the Oracle's JDBC driver Class.forName("oracle.jdbc.driver.OracleDriver"); // connect to the database conn = DriverManager.getConnection(connStrBuf.toString(),
user, password); } catch (Exception e) { L.e(e); conn = null; } } public void invalidate() { [37] try { if (conn != null) { conn.close(); } catch (Exception e) { L.e(e); } } } [40] public Service[] getNewOrder(Folder folder, User user) { Service[] children = null; // retrieve all the accessible user services under the user home folder children = folder.getAccessibleUserServices(user); if (folder == user.getHomeFolder()) { // if it is the user's home folder, append group services // to the children array Group[] groups = user.getGroups(); children = (children==null) ? new Service[0]:children; if (groups != null && groups.length > 0) { for (int i = 0; i < groups.length; i++) { Service[] tmpServices = groups[i].getAccessibleUserServices(
user); // Make sure tmpServices is not null tmpServices = (tmpServices == null) ? new Service[0]:tmpServices; Service[] tmp = new Service[children.length +
tmpServices.length]; System.arraycopy(children, 0, tmp, 0, children.length); System.arraycopy(tmpServices, 0, tmp, children.length,
tmpServices.length); children = tmp; } } } if (children == null || children.length == 0) return children; // construct a select // select service_id, count(service_id) from ptg_service_log // where user_id = ? and service_id in (?,?,..?) // group by service_id order by 2 desc try { StringBuffer select = new StringBuffer("select service_id, count(service_id) from ptg_service_log where user_id = "); select = select.append(user.getId()); select = select.append(" and service_id in ("); for (int i =0; i < children.length; i++) { if (i !=0) { select = select.append(", "); } select = select.append(children[i].getId()+ " "); } select = select.append(") "); select=select.append(" group by service_id order by 2 desc"); Statement st = conn.createStatement(); L.e("select = "+select.toString()); ResultSet res = st.executeQuery(select.toString()); Service[] newOrders = new Service[children.length]; int j=0; ModelServices modelServices= MetaLocator.getInstance()
.getModelServices(); while (res.next()) { newOrders[j++] = modelServices.lookupService(res.getLong(1)); } res.close(); st.close(); // append the unvisited services at the end for (int k=0; k < children.length; k++) { int m=0; for (m=0; m <j; m++) { if (newOrders[m].getId() == children[k].getId()) { // already in the new order list break; } } if (m ==j) { //not in the new order list newOrders[j++] = children[k]; } } return newOrders; } catch (Exception e) { L.e(e); return children; } } }

This class encapsulates the folder renderer's context information, particularly the database connection object.

This class implements the oracle.panama.rt.ManagedContext. When the session expires, the ManagedContext object is invalidated through the invalidate method between line 37 and line 40. The ManagedContext object is invalidated so that the database connection can be closed.

In addition to the Java source code, you should also modify the following entry in the System.properties file to

locator.folder.renderer.hook.class=hook.MyFolderRenderer

6.8.1.2 Example 2

The second example is also a hook example, but it takes advantage of the policy concept. The MyAuthenticator first examines the "badguys" table to make sure the login Wireless Edition user is not in the table. If the user is in the table, then the hook rejects the login request. Otherwise, it resumes the default policy implementation in lines 42 and 44.

package hook;

import oracle.panama.rt.hook.AuthenticationHook;
import oracle.panama.rt.hook.AuthenticationPolicy;
import oracle.panama.rt.hook.AuthenticationContext;
import oracle.panama.rt.hook.AuthenticationException;
import oracle.panama.rt.hook.AuthenticationFailOverException;
import oracle.panama.rt.Request;
import oracle.panama.rt.hook.AuthenticationContext;
import oracle.panama.core.util.Locator;
import oracle.panama.core.admin.L;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;


public class MyAuthenticator implements AuthenticationHook {

private static MyAuthenticator myAuthenticator;
private Connection conn;
private PreparedStatement st;

private MyAuthenticator () { 
    try {
        // lookup the db.connect.string in the panama's System.properties file
        String connectString = 
Locator.getInstance().getResource().getSystem().getString("db.connect.string", 
"");
        // constrct the JDBC connect string, always use the THIN driver for
        // simplicity
        int i = connectString.indexOf('/');
        String user = connectString.substring(0, i);
        int j = connectString.indexOf('@', i+1);
        String password = connectString.substring(i+1, j);
        String dbname = connectString.substring(j+1);
        StringBuffer connStrBuf = new StringBuffer("jdbc:oracle:thin:");

        connStrBuf.append("@");
        connStrBuf.append(dbname);
        // load the Oracle's JDBC driver
        Class.forName("oracle.jdbc.driver.OracleDriver");

        // connect to the database
        conn = DriverManager.getConnection(connStrBuf.toString(), user, 
password);
        st = conn.prepareStatement("select name from badguys where name = ?");
    } catch (Exception e) {
        L.e(e);
        conn = null;
    }
}

public static AuthenticationHook getInstance() {
    if (myAuthenticator == null) {
        synchronized (MyAuthenticator.class) {
            if (myAuthenticator == null) {
                myAuthenticator = new MyAuthenticator();
            }
        }
    }
    return myAuthenticator;
}

public AuthenticationContext authenticate(String name, String passwd,
Request request) throws AuthenticationException,
AuthenticationFailOverException { boolean badguy; if (conn == null) return AuthenticationPolicy.authenticateUser(name, passwd, request); try { st.setString(1, name); ResultSet rs = st.executeQuery(); badguy = rs.next(); } catch (Exception e) { L.e(e); return AuthenticationPolicy.authenticateUser(name,
passwd, request); [42] } if (badguy) { L.e(name+ " is an intruder!"); throw new AuthenticationException(name+" is an intruder!"); } else { return AuthenticationPolicy.authenticateUser(name,
passwd, request); [44] } } }

You should also modify the following entry in the System.properties file in the following manner:

locator.authentication.hook.class =hook.MyAuthenticator

6.8.2 Event Listener Example

The following partial example (the complete "runable" example is under the WE_HOME\sample\listener directory) illustrates how to implement a RequestListener. This RequestListener simply writes the request related information to a log file.

6.8.2.1 Implementing the RequestListener Interface

The RequestListenerSample source file is as follows:

/*
*
$Copyright:
Copyright (c) 1999 Oracle Corporation all rights reserved
$
*/

package listener;

import oracle.panama.rt.Request;
import oracle.panama.rt.Response;
import oracle.panama.rt.Session;
import oracle.panama.rt.AttributeCategory;

import oracle.panama.rt.event.RequestEvent;
import oracle.panama.rt.event.ResponseEvent;
import oracle.panama.rt.event.SessionEvent;
import oracle.panama.rt.event.RequestListener;
import oracle.panama.rt.event.ResponseListener;
import oracle.panama.rt.event.SessionListener;
import oracle.panama.rt.event.AbortServiceException;

/**
* Sample request listener to process request event
*
* @version   $Revision: 1.00 $
* @since     Wireless Edition Release 1.0
*/
public class RequestListenerSample implements RequestListener {       [31]

private final static String BEFORE_REQUEST    = "L__L1";
private final static String REQUEST_BEGIN     = "L__L2";
private final static String SERVICE_BEGIN     = "L__L3";
private final static String SERVICE_END       = "L__L4";
private final static String TRANSFORM_BEGIN   = "L__L5";
private final static String TRANSFORM_END     = "L__L6";
private final static String REQUEST_END       = "L__L7";
private final static String AFTER_REQUEST     = "L__L8";

/**
* The event notification before the start of request              [37]
* @param    an event
*/                                                              [39]
public void beforeRequest(RequestEvent event) throws AbortServiceException {
ListenerRegistrationHookSample.println("BEFORE REQUEST -- " + event.toString() + 
"---" + event.getTimeStamp());
event.put(BEFORE_REQUEST, new Long(event.getTimeStamp()));
event.getRequest().setAttribute(AttributeCategory.RUNTIME, BEFORE_REQUEST, new 
Long(event.getTimeStamp()));

}

/**
* The event notification when request begins
* @param    an event
*/

public void requestBegin(RequestEvent event) throws AbortServiceException {
      ListenerRegistrationHookSample.println("REQUEST BEGIN -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(REQUEST_BEGIN, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME, REQUEST_BEGIN,
new Long(event.getTimeStamp())); } /** * The event notification when service begins * @param an event */ public void serviceBegin(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("SERVICE BEGIN -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(SERVICE_BEGIN, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME, SERVICE_BEGIN,
new Long(event.getTimeStamp())); } /** * The event notification when service end * @param an event */ public void serviceEnd(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("SERVICE END -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(SERVICE_END, new Long(event.getTimeStamp())); [62] event.getRequest().setAttribute(AttributeCategory.RUNTIME, SERVICE_END,
new Long(event.getTimeStamp())); } /** * The event notification when transform begins [65] * @param an event */ public void transformBegin(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("TRANSFORM BEGIN -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(TRANSFORM_BEGIN, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME,
TRANSFORM_BEGIN, new Long(event.getTimeStamp())); } /** * The event notification when transform end * @param an event */ public void transformEnd(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("TRANSFORM END -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(TRANSFORM_END, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME,
TRANSFORM_END, new Long(event.getTimeStamp())); } /** * The event notification when request ends * @param an event */ public void requestEnd(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("REQUEST END -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(REQUEST_END, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME,
REQUEST_END, new Long(event.getTimeStamp())); } /** * The event notification when request error happens * @param an event */ public void requestError(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("REQUEST ERROR -- " +
event.toString() + "---" + event.getTimeStamp()); } /** * The event notification after the end of request * @param an event */ public void afterRequest(RequestEvent event) throws AbortServiceException { ListenerRegistrationHookSample.println("AFTER REQUEST -- " +
event.toString() + "---" + event.getTimeStamp()); event.put(AFTER_REQUEST, new Long(event.getTimeStamp())); event.getRequest().setAttribute(AttributeCategory.RUNTIME,
AFTER_REQUEST, new Long(event.getTimeStamp())); // start logging the object cached in the Request ListenerRegistrationHookSample.println("logging the object cached in the request"); Long beforeRequestTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, BEFORE_REQUEST); if (beforeRequestTime != null) ListenerRegistrationHookSample.println("BEFORE REQUEST: " +
beforeRequestTime.longValue()); Long requestBeginTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, REQUEST_BEGIN); if (requestBeginTime != null) ListenerRegistrationHookSample.println("REQUEST BEGIN: " +
requestBeginTime.longValue()); Long serviceBeginTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, SERVICE_BEGIN); if (serviceBeginTime != null) ListenerRegistrationHookSample.println("SERVICE BEGIN: " +
serviceBeginTime.longValue()); Long serviceEndTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, SERVICE_END); if (serviceEndTime != null) ListenerRegistrationHookSample.println("SERVICE END: " +
serviceEndTime.longValue()); Long transformBeginTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, TRANSFORM_BEGIN); if (transformBeginTime != null) ListenerRegistrationHookSample.println("TRANSFORM BEGIN: " +
transformBeginTime.longValue()); Long transformEndTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, TRANSFORM_END); if (transformEndTime != null) ListenerRegistrationHookSample.println("TRANSFORM END: " +
transformEndTime.longValue()); Long requestEndTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, REQUEST_END); if (requestEndTime != null) ListenerRegistrationHookSample.println("REQUEST END: " +
requestEndTime.longValue()); Long afterRequestTime = (Long) event.getRequest().getAttribute(
AttributeCategory.RUNTIME, AFTER_REQUEST); if (afterRequestTime != null) ListenerRegistrationHookSample.println("AFTER REQUEST: " +
afterRequestTime.longValue()); if ((afterRequestTime != null) && (beforeRequestTime != null)) ListenerRegistrationHookSample.println("REQUEST DURATION: " +
(afterRequestTime.longValue() -
beforeRequestTime.longValue())); // start logging the object cached in the RequestEvent ListenerRegistrationHookSample.println("logging the object cached in the request event"); beforeRequestTime = (Long) event.get(BEFORE_REQUEST); if (beforeRequestTime != null) ListenerRegistrationHookSample.println("BEFORE REQUEST EVENT: " +
beforeRequestTime.longValue()); requestBeginTime = (Long) event.get(REQUEST_BEGIN); if (requestBeginTime != null) ListenerRegistrationHookSample.println("REQUEST BEGIN EVENT: " +
requestBeginTime.longValue()); serviceBeginTime = (Long) event.get(SERVICE_BEGIN); if (serviceBeginTime != null) ListenerRegistrationHookSample.println("SERVICE BEGIN EVENT: " +
serviceBeginTime.longValue()); serviceEndTime = (Long) event.get(SERVICE_END); if (serviceEndTime != null) ListenerRegistrationHookSample.println("SERVICE END EVENT: " +
serviceEndTime.longValue()); transformBeginTime = (Long) event.get(TRANSFORM_BEGIN); if (transformBeginTime != null) ListenerRegistrationHookSample.println("TRANSFORM BEGIN EVENT: " +
transformBeginTime.longValue()); transformEndTime = (Long) event.get(TRANSFORM_END); if (transformEndTime != null) ListenerRegistrationHookSample.println("TRANSFORM END EVENT: " +
transformEndTime.longValue()); requestEndTime = (Long) event.get(REQUEST_END); if (requestEndTime != null) ListenerRegistrationHookSample.println("REQUEST END EVENT: " +
requestEndTime.longValue()); afterRequestTime = (Long) event.get(AFTER_REQUEST); if (afterRequestTime != null) ListenerRegistrationHookSample.println("AFTER REQUEST EVENT: " +
afterRequestTime.longValue()); if ((afterRequestTime != null) && (beforeRequestTime != null)) ListenerRegistrationHookSample.println("REQUEST DURATION EVENT: " +
(afterRequestTime.longValue() - beforeRequestTime.longValue())); } }

Line 31 in the above code example declares the implementation of the oracle.panama.rt.event.RequestListener interface.

6.8.2.2 Register the Request Listener Entry in the System.properties File

Append RequestListenerSample to locator.request.listener.classes entries as follows:

locator.request.listener.classes = ...,..., RequestListenerSample

You can register multiple listener classes for each event category. Each listener class name is separated by a comma.

6.8.2.3 Register the RequestListener with Each Request Object

You should implement the ListenerRegistrationHook to register your request listener object whenever a new request is created. See the code section between line 62 and line 65 in the code example below.

Your new registration hook class has to implement the oracle.panama.rt.event.ListenerRegistrationHook interface as in line 31 in the code example below. The class also needs to implement the Singleton pattern. See the code section between lines 37 and 39 in the code example below.

/*
 * $Header: 
/home/cvsroot/panama/src/core/oracle/panama/rt/common/ListenerRegistration.java
 *
 * $Copyright:
 *             Copyright (c) 1999 Oracle Corporation all rights reserved
 * $
 */

package listener;

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.FileNotFoundException;
import java.net.URL;

import oracle.panama.rt.Request;
import oracle.panama.rt.Response;
import oracle.panama.rt.Session;

import oracle.panama.rt.event.RequestListener;
import oracle.panama.rt.event.ResponseListener;
import oracle.panama.rt.event.SessionListener;

import oracle.panama.rt.hook.ListenerRegistrationHook;
import oracle.panama.rt.hook.ListenerRegistrationPolicy;

/**
Sample listener hook implementation to register/unregister SessionListener, 
RequestListner, ResponseListener
Default event listeners, such as SystemLogger, can be registered through 
ListenerRegistrationPolicy

*
to try this sample, you need to add the following line in jserv.properties
*
wrapper.classpath=i:\panama\pub\examples\sample
*
* @version   $Revision: 1.00 $
* @since     Wireless Edition Release 1.0
*/
public final class ListenerRegistrationHookSample implements      [31] 
ListenerRegistrationHook {                      

public final static String LISTENER_LOG_FILE = "ListenerSample.log";
public static PrintStream logPrint = System.out;

private SessionListener sessionListener = null;
private RequestListener requestListener = null;
private ResponseListener responseListener = null;

private static ListenerRegistrationHookSample singleInstance = null;

public static ListenerRegistrationHookSample getInstance() {       [37]
     if (singleInstance == null) {
          singleInstance = new ListenerRegistrationHookSample();
     }
     return singleInstance;
}                                                                  [39]

public void finalize() {
     logPrint.println("RegistrationHook is deallocated -- " +
System.currentTimeMillis()); logPrint.flush(); logPrint.close(); } public static void println(String str) { logPrint.println(str); logPrint.flush(); } private ListenerRegistrationHookSample() { URL url = ClassLoader.getSystemResource(
"listener/ListenerRegistrationHookSample.class"); if (url != null) { String filePath = url.getFile(); int lastSlash = filePath.lastIndexOf("/"); filePath = filePath.substring(1, lastSlash); filePath = filePath + "/" + LISTENER_LOG_FILE; try { FileOutputStream logFile = new FileOutputStream(filePath, true); logPrint = new PrintStream(logFile); } catch (Exception fnfe) { fnfe.printStackTrace(); } } logPrint.println("RegistrationHook is initialized -- " +
System.currentTimeMillis()); logPrint.flush(); } /** * instantiate the sample session listener class and register to sesson * @param request an incoming request * @param session a new session to register listeners */ public void registerSessionListeners(Request request, Session session) { sessionListener = new SessionListenerSample(); if (sessionListener != null) { session.addSessionListener(sessionListener); } // optional, register default session listeners ListenerRegistrationPolicy.registerSessionListeners(request, session); } /** * instantiate the sample request listener class and register to request * @param request a new request to register listeners */ public void registerRequestListeners(Request request) { [62] requestListener = new RequestListenerSample(); if (requestListener != null) { request.addRequestListener(requestListener); } //optional, register default request listeners ListenerRegistrationPolicy.registerRequestListeners(request); } /** [65] * instantiate the sample response listener class and register to response * @param request an incoming request * @param session an existing session * @param response a new response to register listeners */ public void registerResponseListeners(Request request, Response response) { responseListener = new ResponseListenerSample(); if (responseListener != null) { response.addResponseListener(responseListener); } // optional, register default response listeners ListenerRegistrationPolicy.registerResponseListeners(request, response); } /** * unregister the listeners from session. * @param session a session to unregister listeners */ public void unregisterSessionListeners(Session session) { if (sessionListener != null) { session.removeSessionListener(sessionListener); } //optional, unregister default session listeners ListenerRegistrationPolicy.unregisterSessionListeners(session); } /** * unregister the listeners from request. * @param request a request to unregister listeners */ public void unregisterRequestListeners(Request request) { if (requestListener != null) { request.removeRequestListener(requestListener); } //optional, unregister default request listeners ListenerRegistrationPolicy.unregisterRequestListeners(request); } /** * unregister the listeners from response. * @param response a response to unregister listeners */ public void unregisterResponseListeners(Response response) { if (responseListener != null) { response.removeResponseListener(responseListener); } //optional, unregister default response listeners ListenerRegistrationPolicy.unregisterResponseListeners(response); } }

6.8.2.4 Modify the Event Mask

Since the sample request is interested in all the request events, you should make sure that the event mask for all the request-related events is set to true in the System.properties file as follows:

event.before.request=true
event.request.begin=true
event.request.end=true
event.service.begin=true
event.service.end=true
event.transform.begin=true
event.transform.end=true
event.request.error=true
event.after.request=true



Go to previous page Go to next page
Oracle
Copyright © 2001 Oracle Corporation.

All Rights Reserved.

Library

Solution Area

Contents

Index