Oracle9i Application Server Wireless Edition Developer's Guide Release 1.1 Part Number A86700-01 |
|
This document describes the Wireless Edition Runtime API. Each section of this document presents a different topic. These sections include:
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:
oracle.panama.rt
provides the interfaces to the essential Runtime objects for status examination.
oracle.panama.rt.event
provides the interface to monitor the runtime execution sequence based on the Java event model.
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.
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:
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
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.}
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.
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.
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:
target="___REQUEST_NAME__?___SESSION__& PAoid=__PAoid__"
Then after the substitution of the Runtime variables, the result becomes:
target="/ptg/rm?PAsid=ukAj6hH& PAoid=254"
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
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
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&
PAckey=6!">Scott</SimpleMenuItem> c. <SimpleMenuItem
target="/p2g/rm?PAsid=BVlcv&
PAckey=7!">Tiger</SimpleMenuItem> d. </SimpleMenu> 2. </SimpleContainer> iv. </SimpleResult> d. </Result> 2.</ServiceRequest>
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.
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:
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.
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.
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:
before request
request begin
request end
service begin
service end
transform begin
transform end
after request
request error
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.
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
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
The following guidelines describe how to set up the customized Event Listener:
RequestListener
, ResponseListener
, or SessionListener
interface.
ListenerRegistrationHook
interface.
ListenerRegistrationHook
.
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:
beforeRequest(RequestEvent)
beforeSession(SessionEvent)
requestBegin(RequestEvent)
sessionBegin(SessionEvent)
serviceBegin(RequestEvent)
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
The following sections discuss the request factory pattern and the Runtime execution reference model for it.
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.
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.
createRequest(HttpServletRequest,HttpServletResponse)
ParmImpl
submits an HTTP request containing input parameters to the RequestFactory
to create the Request
object.
registerRequestListeners(Request)
Runtime
passes the object to the ListenerRegistrationHook
to let it register listeners.
beforeRequest(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
execute()
ParmImpl
executes the newly created Request
object, which indicates to Runtime
that a sequence of activities will follow.
findDeviceType(String)
Runtime
dispatches to the DeviceIdentificationHook to determine the device model.
createSessionId()
Runtime
dispatches to the SessionIdHook
to create a new session id for the PAsid
parameter.
createSession()
Runtime
creates a new Session
for the given session id.
registerSessionListeners(Request,Session)
Runtime
passes the new Session
to the ListenerRegistrationHook
to let it register the session listeners.
beforeSession(SessionEvent)
The event source Session
issues a notification to each of the SessionListeners
, passing the SessionEvent
object.
parseInputParameters()
Runtime
parses the URL in the HTTP request and extracts the input parameters.
requestBegin(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
authenticate(String,String,Request)
Runtime
dispatches to the AuthenticationHook
to authenticate the user.
getSubscriberId(Request,Session)
Runtime
dispatches to the SubscriberIdHook
to obtain the subscriber id of the user, which can be used by the CallerLocationHook
.
sessionBegin(SessionEvent)
The event source Session
issues a notification to each of the SessionListeners
, passing the SessionEvent
object.
getCurrentLocation(Request)
Runtime
dispatches to the CallerLocationHook
to determine the location of the caller (mobile device).
authorize(User,Service,Request,AutheticationContext)
Runtime
dispatches to the AuthorizationHook
to authorize the requested service.
serviceBegin(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
invoke(ServiceContext)
Runtime
invokes the service in the repository, passing the ServiceContext
object.
serviceEnd(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
transformBegin(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
process(Request,Element)
Runtime
dispatches to the PreProcessorHook
to process the SimpleResult output of the service.
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.
transform(Element,LogicalDevice)
Runtime
invokes the device ResultTransformer to transform the SimpleResult to the device's markup language.
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".
process(Request,Response,String)
Runtime
dispatches to the PostProcessorHook
to process the device markup page to produce the FinalResult.
transformEnd(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
, passing the RequestEvent
object.
writeContent()
Runtime
writes the content to the HTTPServletResponse
.
requestEnd(RequestEvent)
The event source Request
issues a notification to each of the RequestListeners
.
invalidate()
ParmImpl
invalidates the Request
object.
afterRequest(RequestEvent)
The event source Request
issues a final notification to the RequestListeners
, passing the RequestEvent
object.
There are two different kinds of system parameters: static and derived parameters. The following sections discuss these two types of 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:
sc.getParameters(X)
where X is the parameter name.
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)); }
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
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.
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() { .... } ... }
Make sure you have included the panama.zip in your Java classpath during compilation.
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
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>
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.
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.
When implementing the new listeners and hooks, consider also the following points:
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.
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.
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.
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.
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.
The following examples are available in the respective subdirectories under PANAMA_HOME\sample.
The following two examples illustrate how you can develop user-defined hooks:
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
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
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.
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.
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.
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); } }
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
|
Copyright © 2001 Oracle Corporation. All Rights Reserved. |
|