Oracle9iAS Wireless Developer's Guide Release 2 (9.0.2) Part Number A90485-02 |
|
This chapter discusses how you can use the Oracle9iAS Wireless to develop and deliver mobile services. It explains how to create adapters and transformers, customize your mobile portals at various levels (JavaServer Pages, Portal API, Data Model API, and Runtime API), extend and customize the functional components in the Oracle9iAS Wireless, and work with the XML formats that the Oracle9iAS Wireless uses. Sections include:
Oracle9iAS Wireless provides a powerful, complete and integrated platform for developing, testing and deploying mobile applications. The Oracle9iAS Wireless core, runtime, tools are built top of proven Oracle technologies including OC4J Container, Distributed Configuration Management (DCM), Enterprise Management Daemon (EMD), XML, Oracle Internet Directory (OID), Single Sign-On Server (), Oracle Process Manager (OPMN), WebCache, and Oracle9i. Oracle9iAS Wireless in-house and community development and testing tools make the mobile application development easier. Oracle9iAS Wireless Server can take mobile applications to be deployed to any mobile network, and accessible from any device through any gateway.
As depicted in the above diagram, Oracle9iAS Wireless provides the following wireless development/deployment tool sets:
The following figure (divided into halves for easier viewing) shows how the above Wireless Platform and Tools are deployed physically in terms of processes and relationships with key components of a complete mobile application solution. The wireless-specific components are within the dark-blue rectangle. As the wireless component is an integral part of Oracle9iAS, it seamlessly integrates with other Oracle9iAS components including WebCache, Oracle Http Server, SSO, OID, EM, and Oracle Portal (highlighted with light-blue background color).
Oracle9iAS Wireless components contribute maximally 7 process groups on any machine on which the wireless component is installed and configured.
Oracle9iAS Wireless platform can receive requests from any device via any protocol and deliver content to any device via any protocol. The key request execution flows are:
Http Request Flow--Many devices with certain gateway support can request service through HTTP protocol. These devices include WAP phones with WAP gateways, fixed voice lines with VoiceXML gateways, and others. As illustrated in the above process architecture diagram:
Async Request Execution Flow--Wireless server can also process requests from non-HTTP based devices, such as SMS device, Pager, Email and etc. Here is the request execution flow:
Push Request Execution Flow--Wireless platform can also push any message to any device via different protocols. Out-of-the-box, any message can be pushed out as a SMS message, an email, a voice mail, a fax or to Oracle Mobile Message Gateway. The push request execution flow is as follows:
The default installation configures the installed wireless component to work with the Oracle HTTP Server, WebCache on the local machine. The following mount points are added in the configuration file of the Oracle HTTP Server on the local machine:
/ptg -- for wireless web server
/async -- for async server
/modules - for module server
/webtool - for accessing webtools
/studio - for mobile studio
/customization - for accessing customization portal
/push -- for publishing the push message
If using an Oracle HTTP Server on a different machine (instead of on the local machine), you must manually configure the Oracle HTTP Server. For instructions on configuration, see Oracle9iAS Wireless Getting Started and System Guide.
By default, all the above mounting points are exposed. Comment out these mounting points (so you are not publishing the configuration file from the Oracle HTTP Server).
By default, the Wireless ToolSet and Wireless Runtime Server process groups are configured with single process only. See Oracle9iAS Wireless Getting Started and System Guide to learn how to configure them in load balancing mode.
Files under ORACLE_HOME/wireless/lib belong to Oracle9iAS Wireless. They are:
Oracle9iAS Wireless depends upon the following jar/zip files included in the Oracle9iAS Wireless common technology stack:
This section describes Oracle9iAS Wireless integration with Single Sign-On (SSO) and Oracle Internet Directory (OID) server. The IAS v902 SSO is used by all IAS v902 components for user authentication, and OID is the single place for storing all the User related information.
This integration provides:
Users authenticate only once, and can access any SSO partner application. For example, a user authenticated by the Oracle9iAS Wireless server can access any SSO-enabled Partner Application (such as Oracle Portal) without authenticating again.
The following scenarios illustrate interactions between Oracle9iAS Wireless server and the SSO server.
The Oracle9iAS Wireless server authenticates a user when the user sends an explicit Login Request (identified by URL parameter PAlogin=true), or tries to access a private service.
In Oracle9iAS Wireless-v902, the first request to the device portal (http://Oracle9iAS WirelessServer:port/ptg/rm) returns the home page of the anonymous user (Guest), or the home page of the identified virtual User. From that point, the user can access public services or can do an explicit login to access their private services. The unauthenticated user can execute HTTP Adapter-based public services, which points to an SSO-based partner application (such as Oracle Portal). The partner application may complete the SSO-based user authentication.
All Web-based Oracle9iAS Wireless applications (such as Customization) will authenticate users using mod_osso, which is a module plugged into Oracle HTTP Server. All of the Web-based Oracle9iAS Wireless applications running behind Oracle HTTP Server are treated as a single partner application. Users can access any of the applications after single sign-on.
The device portal uses the value of the HTTP header OssoUser_Guid to identify the mod_sso authenticated user.
Voice authentication is accomplished by Oracle9iAS Wireless (locally) using the account number and the PIN of the user. Note that an authenticated user accessing external SSO partner applications from a voice device must re-authenticate (using username and password).
Oracle9iAS Wireless server participates in the SSO Global Logout. The following steps detail the interactions between Oracle9iAS Wireless, SSO Server and Partner Applications.
The user can click Oracle9iAS Wireless Logout to sign off.
The authenticated user can click on the logout link on the page returned by the SSO-based partner application. In this case, the logout link will point to the SSO sign-off URL.
Since all Web-based Oracle9iAS applications are authenticated through mod_osso, and are treated as a single partner application, logout from any application triggers global sign-off and none of the applications will be accessible until the user signs on through mod_osso again.
In this release, user information is stored centrally in OID. The SSO server uses an OID repository to authenticate users. The following table shows the attribute mapping between PanamaUser (stored in Oracle9iAS Wireless repository) and orclUserV2 user attributes (stored in OID).
iASv902 administrators can use tools (such as Delegated Administrative Services [DAS]), to create a new User in OID or to modify attributes of an existing user. Alternatively, Oracle9iAS Wireless customers can implement their own user administrator tool to create/modify/delete users using Oracle9iAS Wireless model APIs.
The user information is synchronized between Oracle9iAS Wireless and OID repositories using the following mechanisms:
Oracle9iAS Wireless synchronizes user information (stored in the Wireless repository) with OID after SSO authentication.
Note: The reason for synchronizing User attributes with OID is that the PL/SQL notification mechanism does not guarantee real time notifications. |
The Oracle9iAS Wireless installation registers a PL/SQL procedure with OID. The PL/SQL procedure is invoked when a user is modified or deleted in OID.
The ModelFactory.createUser() method creates a corresponding User in the OID repository.
The User.set methods update the corresponding User entry in OID for all the attributes. The following table shows the attribute mapping between PanamaUser (stored in Oracle9iAS Wireless repository) and orclUserV2 user attributes (stored in OID). The User.delete() method removes the corresponding User from the OID repository. The current semantics of commit is preserved for the User modifications.
In Oracle9iAS Wireless integration mode, when you create a user through Webtool User Management, the request is first redirected to OID DAS (Delegated Administration Service), for entering Oracle9iAS User Common Attribute Values. After that, the request is redirected back to the Webtool User Management page for entering Wireless-specific attribute values.
The same applies for editing a registered Wireless user. The user is first edited through DAS and then through Webtool User Management.
Oracle9iAS Wireless is integrated with Oracle WebCache to improve page rendering performance and scalability. It must be clarified at the outset that WebCache is not deployed in the traditional sense with Oracle9iAS Wireless. WebCache is usually deployed in front of web-servers serving HTML content, and interacting with HTML clients and the web-server to cache dynamic content. However, with Oracle9iAS Wireless, the wireless runtime determines what content needs to be inserted into WebCache and when to expire content in the cache. WebCache, in this case, acts as a device adaptation cache rather than a reverse-proxy cache.
Since markup content is cached using WebCache, the performance and scalability benefits are due to two factors: reduced device adaptation costs, and significantly reduced adapter invocation costs. The savings in terms of device adaptation costs are due to the fact that content that can be shared across users and sessions is essentially transformed only once (per logical device) from its Mobile XML format. Secondly, since the content is not generated every time by an adapter, the total adapter invocation cost is significantly reduced for a site that has a large subset of cacheable pages.
In this case, an incoming request from a client is for a page that has been cached by webcache.
To cache dynamic content, it is necessary to enable WebCache in the first place. From the System Manager, click on the Site tab. Under the Administration section, in the Configuration sub-section, click on WebCache Configuration.
The steps detailed above described how to enable caching for a site. For the cache to be of use, it is necessary to enable services to be cacheable.
For any caching mechanism to be effective, it is necessary to perform invalidation of the cache contents at appropriate intervals. Invalidation of wireless content residing in webcache can be either policy-based or asynchronous.
Policy-based Invalidation--It is possible to specify in advance if a page should be cacheable or not. One of the ways to do this is by specifying the invalidation frequency of a service (as in the previous section). When a page is inserted into the cache, the invalidation frequency of the service it belongs to is taken into account while determining how long the page should live in the cache.
It is also possible to dynamically specify the cacheability of a page. This is done at the content-source. If the page is to be specified as cacheable, the SimpleResult element should have a SimpleMeta child element. This element has a required attribute `cache', which when set to `yes', enables caching for the page and when set to `no' disables caching. An optional attribute to be used in conjunction with a `yes' value for the `cache' attribute is `ttl'. This can be used to specify in seconds the number of seconds the page should be cached before expiring it. For example:
<SimpleResult> <SimpleMeta cache="no"/> ..... </SimpleResult>
results in the page being non-cacheable, as below:
<SimpleResult> <SimpleMeta cache="yes" ttl="300"/> .... </SimpleResult>
results in the page being cached for 300 seconds.
Apart from using the SimpleMeta tag to specify cacheability, it is possible to use standard HTTP cache-control headers and ESI headers to specify cacheability for a page. Refer to your documentation on WebCache on how to specify cacheability using ESI headers.
The order in which cacheability for a given page is evaluated is as follows:
Asynchronous Invalidation--Despite specifying the cacheability policy for a page at the time of service creation or during the generation of the page, it may be necessary to explicitly invalidate content in the cache. It is possible to invalidate and refresh content in the cache based on a master service or a device.
From System Manager, click on the Site tab. Under the Administration section, in the Configuration sub-section, click on either `Refresh webcache - Master service' or `Refresh webcache - Device'.
If webcache is reinstalled on a different machine/port the WebCache settings must be reconfigured as detailed in the configuration section above.
If the wireless instance is reinstalled on a different machine, the location of the wireless instance should be modified in the `Application Servers' of WebCache's administration console. See the WebCache Configuration Guide for details on how to perform this task.
In this section we shall build a sample service that is cacheable using webcache. We shall also explore the means to control the cacheability of such a service dynamically.
The sample service displays the current time and therefore immediately demonstrates the cached status of the page. We follow the steps detailed below to create the service:
<%@ page language="java" %> <%@ page import="java.text.SimpleDateFormat"%> <%@ page import="java.util.Date"%> <%@ page session="false" %> <%@ page contentType="text/html; charset=iso-8859-1" %> <SimpleResult> <SimpleContainer> <SimpleText> <SimpleTextItem> <% Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd G 'at' hh:mm:ss a zzz"); %> <%=formatter.format(date)%> </SimpleTextItem> </SimpleText> </SimpleContainer> </SimpleResult>
Let us assume that this page is deployed at the URL: http://mycontent-server.oracle.com/dateserv.jsp
The service is now accessible from the device portal. We can see that the time-stamp displayed as a result of invoking the DateService service does not change for 40 seconds, indicating that the service has been cached for 40 seconds and invalidated after. Please note that after a page in the cache has expired, the content is fetched from the content source only on a demand basis, i.e. after 40 seconds elapse Webcache will not refresh the content immediately, but will do so only after a new request for the page is received.
The time for which the cache can retain the page without refreshing it has been set to 40 seconds during the service creation. However, this value can be changed dynamically at the time of generation of the Mobile XML. This can be done in two ways:
In this case the generated Mobile XML can have a SimpleMeta tag to attain this. Please see the Policy-base Invalidation sub-section in the previous section on how to do this. For our sample service, to ensure that the page is expired after 10 seconds (rather than the default of 40 seconds), the JSP page would be:
<%@ page language="java" %> <%@ page import="java.text.SimpleDateFormat"%> <%@ page import="java.util.Date"%> <%@ page session="false" %> <%@ page contentType="text/html; charset=iso-8859-1" %> <SimpleResult> <SimpleMeta cache="yes" ttl="300"/> <SimpleContainer> <SimpleText> <SimpleTextItem> <% Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd G 'at' hh:mm:ss a zzz"); %> <%=formatter.format(date)%> </SimpleTextItem> </SimpleText> </SimpleContainer> </SimpleResult>
Responses from the content source may contain ESI headers as part of HTTP headers that can dictate cache expiration behavior. Using ESI headers entail no changes to the Mobile XML. The following ESI header expires the page is 30 seconds.
Surrogate-Control: max-age=30+60, content="ESI/1.0"
For more information on ESI headers, please refer to the Webcache Developer's Guide.
Oracle9iAS Portal is a web-based application model for building and deploying e-business portals. It provides a environment for accessing and interacting with enterprise software services and information resources. Portal provides a framework that integrates web-based resources such as web pages, applications, business intelligence reports, and syndicated content feeds, within standardized, reusable information components called portlets.
A portlet is an area of HTML/XML located within a defined area of a Web page. Portlets communicate with the portal through an entity called a provider. Portlets form the fundamental building blocks of a Oracle9iAS Portal page. Each portal page consists of content presented through one or more portlets and links that allow the user to navigate to another page to take some action.
Portlets summarize, promote or provide basic access to an information resource. The portlets allow information resources to be personalized and managed as a service of Oracle9iAS Portal. The portal framework provides additional services including single sign-on, content classification, enterprise search, directory integration, and access control. OraclePortal traditionally has been supporting Desktop/PC Web browsers. Starting in Orcale9iAS releas2.0 OraclePortal, besides support standard web browsers, will enable Oracle9iAS Portal pages to be accessed from wireless devices. OraclePortal, working in conjunction with Oracle9iAS Wireless, automatically transforms the portal page structure that is appropriate for the wireless devices. Portal generates the Page structure in Oracle9iAS Wireless XML, for all request from wireless device, and rendered to the device by Oracle9iAS Wireless. This allows portlets to provide wireless interface using OraclePortal, through Oracle9iAS Wireless.
To enable Oracle9iAS Wireless access to Portal, the Portal must be deployed as a Wireless service in the Oracle9iAS Wireless repository. Each Portal installation is deployed as an HTTP Adapter service in Oracle9iAS Wireless. Multiple Portals may be deployed on a single Wireless instance. The HTTP adapter service accepts a URL as a configuration parameter and must be set to the URL of the Portal's home page. To create a Wireless service, a Master Service definition based on an HTTP adapter must be created using the Oracle9iAS Wireless Webtool. Also, you must create an OraclePortal Service based on the HTTP adapter Master Service.
OraclePortal redirects requests from a Wireless device to an Oracle9iAS Wireless server. The Oracle9iAS Wireless Server accepts the request and invokes the OraclePortal home page over HTTP and accepts the response generated (in Oracle9iAS Wireless XML), from OraclePortal. The XML response, generated by OraclePortal, is then adapted to the native device markup by the Oracle9iAS Wireless server. All further requests and responses between Wireless device and OraclePortal is mediated by the Oracle9iAS Wireless Server.
Wireless devices make the first request to OraclePortal server and Portal redirects the device request to Wireless Server. The Portal appends two parameters to the redirected URL, the two query parameters appended are "PAoid" and "PAhome". Both PAoid and PAhome contain the value of the object id (service-id in the Wireless repository) of the Portal's HTTP adapter service. The syntax of the redirected URL is:
http://9iASWSerrver:port/ptg/rm?PAoid=<OraclePortal object id>&PAhome=<OraclePortal object id>
The PAoid parameter allows the Wireless server to directly launch the Portal home page, without having to navigate through the Wireless server's folder and service hierarchy. The PAhome sets the Portals Home Page as the home page for the current wireless session.
Portlets are owned by entities called Providers, and one Provider can manage one or many portlets. Providers are the backbone behind the Portlets being displayed on each page. Portal supports a Web Provider framework that is written as a web application. It is installed and hosted on a web server and is remote from the Portal. A portlet exposed as a Web Provider can be developed in any web language. A Web Provider communicates with Oracle9iAS Portal using SOAP(XML).
OraclePortal supports a Java based Portal Developer Kit (PDK) framework to develop portlets and services. The Java PDK Framework is a set of services that enable Java programmers to easily create portlets from existing Java-based applications (Java, Java Servlets, and JSPs). It provides an abstraction to handle communication with Oracle9iAS Portal, default classes to simplify portlet creation, and exposes APIs for end-user customization, session storage, security, and logging.
For Wireless devices, OraclePortal will support Portlets that generate Oracle9iAS Wireless XML. To enable wireless access Portlets must generate Oracle9iAS Wireless XML and indicate such capability using the Java PDK framework. The Java PDK framework uses a Provider.xml file to discover the capabilities of the Portlets supported by a Provider. Refer to OraclePortal's PDK-Java User's Guide for more information.
Following is a overview of tags (in the Provider.xml file) that indicates the wireless capabilities of a Portlet.
1.<acceptContentType> Usage: <acceptContentType>text/vnd.oracle.mobilexml</acceptContentType>
This value "text/vnd.oracle.mobilexml" indicates that the portlet is capable of generating Oracle9iAS Wireless XML required for Wireless access. A portlet can be enabled for both HTML (PC Desktop) and Wireless Access by indicating it can accept both the content types such as:
<acceptContentType>text/vnd.oracle.mobilexml</acceptContentType> <acceptContentType>text/html</acceptContentType>
If the Portlet is capable of generating only Oracle9iAS Wireless XML (text/vnd.oracle.mobilexml), then (unless otherwise indicated) the Portlet will transform the Oracle9iAS Wireless XML to HTML for PC Desktop clients.
2.<mobileFlags> Usage: <mobileFlags>MOBILE_ONLY</mobileFlags>
Portlets can set this value to MOBILE_ONLY and hence indicate that this Portlet must be rendered in wireless devices only. This will prevent the default behavior of a Portal to transform Oracle9iAS Wireless XML, generated by the Portlet and rendered to PC Desktop clients.
3.<showLink> Usage:<showLink>true</showLink>
Portal renders all the Portlets on Wireless devices as links. Portlets must set this value to True to be rendered on a wireless device. A value of True allows the Portal to generate a Link, pointing to the Portlet content, on the wireless device.
4.<linkPage> Usage:<linkPage class="oracle.portal.provider.v2.render.http.ResourceRenderer"> <resourcePath>/mypath/mypage.jsp</resourcePath> <contentType>text/vnd.oracle.mobilexml</contentType> </linkPage>
This tags holds the pointer to the resource which generates the required link that is rendered on a wireless device. This resource must generate Oracle9iAS Wireless XML. Below is a sample link page implemented in JSP.
<%@ page session="false" contentType="text/vnd.oracle.mobilexml" %> <SimpleHref target="/mypath/mywireless.jsp" label="Go"> Wireless HelloWorld </SimpleHref>
The new version JPDK has been updated to understand these wireless properties of a Portlet. The JPDK also supports wireless specific request information like location and device information, which can be accessed by the Portlets through the JPDK APIs.
Both OraclePortal and Oracle9iAS Wireless depend on Oracle's SSO solution for user authentication and login. This integration allows the user to invoke protected applications defined on both systems and eliminates multiple login dialog boxes for users.
Oracle9iAS Wireless Server upgrades the session context of a user to an "authenticated" state when any service or application (HTTP Adapter services) validates the user credentials with the SSO server. When OraclePortal, mobile application, validates the credentials of a user with the SSO Server, the session context in Oracle9iAS Wireless is also updated. This allows wireless Portlets deployed on OraclePortal to uses services such as User Location Picker, Routing, Mobile Positioning supported by the Oracle9iAS Wireless Server.
You can use OraclePortal's services to provide a PC Desktop view of your Oracle9iAS Wireless services. You can use Portal's JPDK framework to provide a "showPage" and "editPage", for web-based customizations.
Since the Portal itself can be accessed from a wireless device, you must also provide a mobile Portlet. On a wireless device, the mobile Portlets are rendered as links and can be made to point to a service deployed on the Oracle9iAS Wireless server. You can use Portal's JPDK framework to provide a "linkPage" that generates the appropriate link for your wireless service. To point to a wireless service from a mobile portlet you can use following URL syntax in your Oracle9iAS Wireless XML:
target="___REQUEST_NAME__?___SESSION__&PAoid=<PAoid of Wireless Service>"
The Wireless server will replace all "___<Name>__
" to the correct values at runtime and will invoke a service define in the Oracle9iAS Wireless repository.
The following is a sample link page:
<%@ page session="false" contentType="text/vnd.oracle.mobilexml" %> <SimpleHref target="/___REQUEST_NAME__?PAoid="+PAoid + "&___ SESSION__" label="Go"> My Wireless Service </SimpleHref>
Mobile devices make the first request to OraclePortal server. Portal redirects the device request to Oracle9iAS Wireless Server, over HTTP, and appends two parameters to the redirected URL. The two query parameters are "PAoid" and "PAhome". Both PAoid and PAhome contain the Portal's object/service id. The typical syntax of the redirected URL are:
http://Oracle9iAS WirelessSerrver:port/ptg/rm?PAoid=<OraclePortalServiceid>&PAhome=<OraclePortalService id>
The PAoid parameter allows the Wireless server to directly launch the Portal home page, without having to navigate through the Wireless server's folder and service hierarchy. The PAhome sets the Portals Home Page as the home page for the current wireless session.
The post-installer automatically registers Webtool and Customization as two Oracle Portal Providers. Thus, if an Oracle Portal user selects the two providers he/she will see two portlets: one for Webtool, and one for Customization. If the URL for Webtool or Customization is changed, the provider can be registered from Wireless System Manager, part of Oracle Enterprise Manager. For more information, see Oracle9iAS Wireless Getting Started and System Guide.
Services enable end users to access the functionality of Oracle9iAS Wireless. They represent the link between the content source and the delivery target. Services tie a specific data source (through an adapter) to the different devices.
There are different types of services:
MasterServices provide the basic wireless functionality. They are the actual implementation of the service. Each MasterService is based on one adapter. A MasterService sets values for the adapter init, input and output parameters. Each MasterService creates its own instance of the adapter it uses. Therefore, several services can use the same type of adapter, and each can pass its own service-specific argument values.
It is recommended that you build all MasterServices using the HTTPAdapter. That gives you the flexibility to implement the service business logic using JSPs or other web technologies.
Links are used to further customize existing services by overriding the values of their parameters.
When a Link service is invoked the Wireless server will merge the parameters with the parameters of the service the Link points to, and invoke that service.
Links are also used to better organize services into user service trees. They give you the flexibility to publish the same service under different names and in different folders (different levels in the service tree). If you do not override any parameter values, then invoking the link is the same as invoking the service it points to.
Modules are wireless services with well-known virtual URL (OMP URL, that is, omp://my.module).
Modules can be called from any application or module and may be instructed to return control to another application or module. Calls may be nested to any level. This mechanism of bi-directional linking allows quick applications assembly.
An important difference between a module and a regular service is that the module receives information about the service it needs to return to after it is done. This is not always the caller of the module (the module caller may want the module to return to a different service).
Folders are containers for other services. They are used to better organize user-accessible services into a service tree. The content of a folder is displayed by invoking its rendering service--a special service associated with each folder.
The system rendering service displays the folder child services ordered by the specified sort rule.
Optionally, you can specify icons and audio files to be displayed/played when a service link is displayed in the folder content or when the service is invoked.
An ExternalLink is a wireless service that points to an external resource. The external resource is typically a Web page that serves content in a format supported by the target device.
Oracle9iAS Wireless does not process the content of the ExternalLink target. As a result, ExternalLink services are not available to all targeted devices, as are other Wireless services. In most cases, ExternalLinks are set in the Customization portal by the end user, not in the Service Designer.
There are two type of services in terms of accessibility:
There are different rules that apply to those two type of services.
The user private services are services that reside in the user home service tree. The user can access all of those services. No other user can access those services.
The shared services in contrast are accessed by multiple users. The access is controlled by the User - Group - Service relationship. When you assign a service to a group, all users from that group can access the service.
This section describes how to create and manage Oracle9iAS Wireless transformers.
Logical Device in Oracle9iAS Wireless represents either a physical device, such as an Ericsson mobile phone or an abstract device, such as ASYNC. The Logical Device stores the attributes of the physical device/ browser and device transformers. The Oracle9iAS Wireless server uses the device transformer of the Logical Device associated with the request to transform mobile xml service result to device mark up language.
Each request in Oracle9iAS Wireless is associated with a Logical Device. The Device Detection process, i.e. finding out the Logical device corresponding to a request, is done for each Oracle9iAS Wireless request. Device Detection mechanism is discussed later in the chapter.
The following table lists the Logical Device attributes. These attributes can be retrieved and modified using programmatic java api's. Refer to the javadoc of oracle.panama.model.Device interface.
The above mentioned attributes can be used by the Transformers, Adapters, Folder Renderer hooks or external Http Adapter based Services to generate custom content for the device. The Logical Device attributes are passed to the external Http Adapter based services through HTTP headers. See Section 10.7, "Adapters" for more information.
Oracle9iAS Wireless server is shipped with pre-built Logical Devices. Customers can add additional logical devices or can modify existing Logical Devices if any of their physical devices can not be mapped to an existing Oracle9iAS Wireless Logical Device. Refer to Oracle9iAS Wireless Getting Started and System Guide for details on how to add or modify Logical Devices.
The Device Detection in Oracle9iAS Wireless can be customized by specifying a hook class that implements the interface oracle.panama.rt.hook.DeviceIdentificationHook. The default implementation of the hook is provided in oracle.panma.rt.hook.DeviceIdentificationPolicy class.
The default (built-in) implementation uses the User-Agent String to Logical device mappings, stored in the LogicalDevice model object, to identify the logical device from the request. Note that in previous releases of Oracle9iAS Wireless the User-Agent String to logical device mapping was specified in oracle/panama/core/admin/UserAgents properties file.
The User-Agent string can contain wild card character `*'. For example, the User-Agent String `*DS*' will match all the User-Agent values containing `DS'.
The Device Detection algorithm:
The devices and browsers available in the market today support different image formats, for example, WML devices support wbmp image formats whereas Palm supports gray scale depth 2 image formats. The "image Format Preferences" attribute of Logical device stores all the image mimetype and corresponding file extension supported by the device. This attribute of Logical Device is used by the Transformers to transform the <SimpleImage> mobile xml element.
The mobile xml developer can use the "available" attribute of <SimpleImage> element to specify the list of image file extensions available. The transformer appends the file extension, supported by the device, to the "src" attribute of the <SimpleImage> element. The "src" attribute of <SimpleResult> specifies the location of the image file.
For example, the following <SimpleOracle9iAS Wireless element> specifies that the image_file is available in "gif", "wbmp"and "g2gif" formats.
<SimpleImage src="http://IASWServer:port/image_file" available="gif wbmp g2.gif" />
For devices supporting only g2.gif extension the above <SimpleImage> will get transformed to:
<img src="http://IASWServer:port/image_file.g2.gif">
Oracle9iAS Wireless supports Device Transformers.
The Device Transformers transform mobile xml document to the device markup language. The transformation logic can be implemented in an XSL stylesheet or in Java.
The Result transformers convert content from AdapterResult format to Mobile XML format. The Adapter Result format is an intermediary format layer that enables efficient exchange of user interface independent data. You may use it, for example, to link chained service. A chained service is an Oracle9iAS Wireless service that invokes another service. Result Transformers are deprecated in Oracle9iAS Wireless 9.0.2 version.
The following table lists the attributes stored in the Device Transformer objects. These attributes can be accesses by java programmatic apis' - refer to javadoc of oracle.panama.model.Transformer, oracle.panama.model.JavaTransformer and oracle.panama.model.XSLTransformer interface.
Transformers can implement transformation logic in Java by implementing oracle.panama.rt.xform.RtTransformer interface.
/* * $Copyright: * Copyright (c) 2000 Oracle Corporation all rights reserved * $ */ package oracle.panama.rt.xform; import java.io.Writer; import org.w3c.dom.Element; import oracle.panama.PanamaException; /** * Transform from a XML structure to a device specific content. * * @since Oracle9i Application Server Wireless Edition */ public interface RtTransformer { /** * Transform the simple result XML document into a device specific markup la nguage. * @param element the <code>ServiceContext</code> XML Element to process. * @param out the output writer for the result */ public void transform(Element element, Writer out) throws PanamaException; }
Oracle9iAS Wireless run time calls the transform method of the Java transformer to transform the mobile xml document to device markup. The parameter element contains the ServiceContext, and the device markup result is written to the out parameter of the method. The ServiceContext contains the input parameters of the request, attributes of the logical device corresponding to the request and SimpleResult (mobile xml document).
The class implementing oracle.panama.rt.xform.RtTransformer interface must provide a default constructor (that is, constructor without arguments) and the transform() method should be thread-safe.
The ServiceContext Element passed to the transform() method of RtTransformer class is of the form.
<ServiceRequest> <Arguments> <Inputs>
All the input arguments passed to the Service - this includes the service arguments and other arguments listed below
........ </Inputs> </Arguments> <Result> The mobile xml content ....... <Result> </ServiceRequest>
The input argument is of the form
<name ....>value</name>
where "name" is the name of the input argument and "value" is the value of the input argument.
The following table lists the input arguments other than the service input arguments, which are passed to the Service.
Device transformer logic can be implemented in XSL stylesheet. XSL stylesheets are XML documents that specifies the processing rules for other XML documents. An XSLT stylesheet, like java transformers, is written for a particular mobile XML DTD. When it finds the element in source document, it follows the rules defined for the element to format its content. The ServiceContext element is passed as the source document to the stylesheet.
In this section we will implement a very simple XSL stylesheet which handles only <SimpleTable> mobile xml element. It uses the value of screen width attribute of the logical device, passed as _ScreenWidth element in the ServiceContext, as the width of the generated HTML table. The Stylesheet converts source mobile xml documents to HTML.
Example mobile xml document handled by our custom stylesheet.
<SimpleResult> <SimpleContainer> <SimpleTable> <SimpleRow> <SimpleCol>Row1 column1</SimpleCol> <SimpleCol>Row1 column2</SimpleCol> </SimpleRow> <SimpleRow> <SimpleCol>row2 column1</SimpleCol> <SimpleCol>row2 column2 </SimpleCol> </SimpleRow> </SimpleTable> </SimpleContainer> </SimpleResult>
The stylesheet implementation.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:p2g="http://www.oracle.com/XSL/Transform/java/oracle.panama.core.xform.XSJ ava" exclude-result-prefixes="p2g"> This template matches the root of the document. <xsl:template match="/"> <xsl:apply-templates select="//SimpleResult"/> </xsl:template>
Template for SimpleResult element.
<xsl:template match="SimpleResult"> <HTML> <BODY> <xsl:apply-templates select="SimpleContainer/SimpleTable" /> </BODY> </HTML> </xsl:template>
Template for <SimpleTable> element.
<xsl:template match="SimpleTable"> <TABLE>
The width attribute of the table is set to the value of _ScreenWidth element.
<xsl:attribute name="width"> <xsl:value-of select="//_ScreenWidth" /> </xsl:attribute> <xsl:for-each select="./SimpleRow"> <xsl:apply-templates select="."/> </xsl:for-each> </TABLE> </xsl:template>
Template for SimpleRow element
<xsl:template match="SimpleRow"> <TR> <xsl:for-each select="./SimpleCol"> <xsl:apply-templates select="."/> </xsl:for-each> </TR> </xsl:template>
Template for SimpleCol element.
<xsl:template match="SimpleCol"> <TD> <xsl:value-of select="." /> </TD> </xsl:template> </xsl:stylesheet>
Oracle9iAS Wireless server supports multiple transformers, one for each version of the mobile XML DTD, for Logical Devices. The run time selects the transformer depending on the DTD version specified in the mobile XML Document. All the mobile xml documents should include the following DOCTYPE declaration
<!DOCTYPE SimpleResult PUBLIC "-//ORACLE//DTD SimpleResult x.y.z//EN" "http://xmlns.oracle.com/ias/dtds/SimpleResultxyz.dtd">
The DTD version is specified by the string "x.y.z", where x is incremented for every major revision of DTD, y is incremented for minor revisions, and z can be incremented by the customer for customer specific DTD enhancements.
For backward compatibility all the mobile xml documents, which do not contain the DOCTYPE declaration, will be assumed to be confirming to 1.0.0 version (i.e pre 9.0.2 version of the DTD)
The algorithm to find the transformer for a mobile xml document confirming with DTD version x.y.z
The following HelloWorld.xml confirms to DTD version 1.1.0 (which is the current version of the DTD).
<?xml version = "1.0" encoding = "UTF-8" standalone="yes" ?> <!DOCTYPE SimpleResult PUBLIC "-//ORACLE//DTD SimpleResult 1.1.0//EN" "http://xmlns.oracle.com/ias/dtds/SimpleResult_1_1_0.dtd"> <SimpleResult> <SimpleContainer> <SimpleText> <SimpleTextItem>Hello World </SimpleTextItem> </SimpleText> </SimpleContainer> </SimpleResult>
Oracle9iAS Wireless presents a framework to develop mobile applications to be accessed "from anywhere, from any device, using any protocol". The Asynchronous Server Kernel, also known as ASK makes the "any protocol" promise possible.
Conventionally, the entry point into an application server is through the HTTP protocol. This limits applications built on an application server to only clients with Web capability. This server restriction is a problem for mobile market users because the vast majority of mobile users do not have, or are not enabled with Web access. These users are almost certain to have some kind of message capabilities though (such as e-mail, SMS, etc.). Now the dilemma is whether one should build applications for such users specifically, depending on their capability, or ignore them because the application server just cannot deal with the mobile market.
With Oracle9iAS Wireless, the dilemma is solved for developers, without them doing anything at all. With the introduction of ASK, mobile applications can be accessed through the usual HTTP protocol, as well as any other messaging protocols (such as e-mail, SMS, etc.). Developers can focus on building their application logic, and Oracle9iAS Wireless will do the proper connection, session management, and interpretation of user requests. A mobile service is invoked the same way regardless of which protocol handles the incoming requests, offering complete transparency to application developers to allow access to their services.
One of the most obvious challenges is to be able to support multiple protocols. It is not desirable to build the same functionality to work with e-mail, then SMS, then some other protocols. Oracle9iAS Wireless offers access to the same application regardless of the protocol used by clients. Hence the immediate challenge is to be able to support multiple protocols uniformly.
In contrast to the HTTP protocol, (commonly referred to as the synchronous protocol) messaging protocols such as SMS or e-mail are asynchronous. It is asynchronous because unlike HTTP, they are not based on a "request and response" model. A single atomic operation is typically one way. For example, when you use a Web browser, you enter a URL and make the request, then you wait for the result to come back. In messaging protocols (such as SMS) sending a message itself completes one operation. Most applications respond to user requests so HTTP is usually adequate. To enable the same application be accessed through asynchronous protocols presents a challenge on how such behavior can be mimicked with protocols such as SMS or e-mail.
Another big challenge is that most applications are session based; multiple requests and responses are typically required to complete a task. Applications are able to maintain sessions in the Web world because the client, a Web browser, has built in capabilities such as cookies to facilitate session semantics. This is not the case for an e-mail or SMS client. They do not have any such ability built in to support conversational applications.
A Web browser offers a User Interface for navigating through applications (examples include clicking on a hyperlink and traversing through a menu or a series of steps to complete certain functionality). Clients that work with other protocols such as SMS or e-mail typically do not have similar navigation power. The challenge here is to offer similar navigating capability to such clients so that applications can be independent of the protocols.
In the Web world, applications are typically assigned a URL. The URL is how the application can be identified and requested. Clients for messaging are typically plain text devices; there is no convention on how to name a service, but consistency across protocols is needed.
ASK combines functionality of a HTTP server and portions of a Web browser to provide its functionality.
This challenge is a relatively easy one. Built on top of the Oracle9iAS Wireless transport system, support for multiple transport protocols is achieved by the nature of the transport system itself. ASK registers to be an application to the transport system to send and receive messages. It further registers one address for each of the protocols it is serving in order to interact with users on those protocols. For example, it can register ask@yourcomany.com for e-mail and 1234567 for SMS. Then ask@yourcompany.com and 1234567 become the URIs for their respective protocols similar to http://yourcompany.com is to the Web world.
ASK itself does not consider the incoming protocols; it is designed to send and receive messages by the means that it is registered to use. The payload (content) of the messages are what ASK interprets and acts upon.
ASK builds logic similar to an HTTP listener to present synchronous semantics over asynchronous protocols. It achieves this by acting as a client to the service the device requested. ASK makes a request to the service on behalf of the user, waits and processes the response from the service, then formats the response and presents it back to the users. Users have the illusion of a response from an earlier request.
Upon receiving requests from a user, ASK create a session for the user. This allows conversational applications to function. Unlike in HTTP where session info is kept by the browser (or cookie), all session states are kept in the backend by ASK.
ASK transforms elements such as forms or menus, and presents a navigation command for end users. When elements such as forms are returned by a service, ASK retains the format of the form in the backend, and determines the action to take when the form is submitted with all other necessary information. When this user (using the set of command specified by ASK) fills and submits the command, ASK makes a request (based on the current user information stored in ASK) and processes the result again on behalf of the user. You can think of it as if the hyperlinks are stored in the backend when a user clicks on the "face" of the link.
Just as assigning a URL to a service in the Web world, to use ASK, a short name must be assigned to services to be ASK enabled. For example, assume the stock quote service has been assigned the path: /finance/quote and can be accessed as http://mycompany.com/finance/stock. Through Content Manager, a short name can be assigned to it (for example, st.). Now any messages ASK receives that begins with st signals a request for the stock quote service. A user can send st orcl to a site-wide address to which ASK is configured to listen, ask@mycompany.com for e-mail or 1234567 for SMS, and get back the stock quote for Oracle Corporation.
It is also possible to specify service-level address to services. Through Content Manager, one can also associate (e-mail) stock@mycompany.com and (SMS) 123FINANCE to the stock quote service. Once this is done, sending just orcl as an email to stock@mycompany.com or as SMS to 123FINANCE would result in receiving the stock quote of Oracle Corporation.
ASK differentiates the user of a request into two categories: guest or registered. Upon receipt of a user request by ASK, the source address of the request message is used to reverse-lookup a Oracle9iAS Wireless user for authentication. A user object can be located if a user has a device address, registered under his profile, the same as the source address of the request message. The located user object is then bound to a newly authenticated session created by the request. Otherwise, a guest user object is bound to the session. Whichever services are authorized to the user will be accessible to requests issued from the device.
Only those services belonging to the guest group are accessible to a guest user. Accessing a non-guest service triggers a returned form challenging the user for name and password. A valid Oracle9iAS Wireless username/password supplied by the user enables the previous session to be upgraded to an authenticated one with the user object identified by the name to be bound. Alternately, a guest user can log in explicitly through a login command, '!L', to avoid ever being challenged.
As discussed earlier, messaging clients typically only present plain text and do not offer conversational navigation capabilities. Recall that ASK transforms and formats responses from services to a certain presentation to enable such capabilities. ASK includes a set of presentation formats and navigational commands similar to what a Web browser has done for the Web world. Hence when a user invokes services using ASK, he or she would see the response in the format transformed by ASK. Further interactions with ASK would have to comply with the format expected by ASK. In this section we will be discussing commands users can issue to ASK. To issue a command is simply sending a message with the correct format. The command text can be put into a subject line or message body.
These are commands to do service invocation, menu selection and parameters filling. There are no reserved command symbols for the service invocation and form commands. Certain commands, such as form command and menu item selection, can be invoked only when there is a current form/menu state maintained in the user's session. More details on form/menu state will be discussed later in this chapter.
All services that are ASK-enabled should be assigned short names to be accessed by the end user. The short name should be able to uniquely identify a service on the entire site. To invoke a service, a message should be sent to a site-wide address, such as info@oraclemobile.com, to which the ASK is configured to listen. The command line has the format:
<Svc Short Name> <parm1> <parm2> . . .
In the following example, a message is sent to the site-wide address: info@oraclemobile.com, to invoke a stock quote service whose short name is ST. The service requires a stock symbol as its parameter (in this case, ORCL is provided).
Each service may have some device address associated with it. For example, an e-mail address stock@oraclemobile.com can be used to identified a stock service. Since the service has been identified in the destination address of the request message, there is no need to specify the service short name in the command line. Only the service parameters are required in the command line, for example, the stock symbol.
All of the system commands (for example, 'help') can still be issued to the service-associated address. They are interpreted by Async Server in the same way they are sent to the site-wide address.
The way the features are presented is similar to the HTTP model. A service invocation may trigger the return of a message with the menu. Each menu item is prefixed with a number. Users are able to make selections by issuing another message in which the message content contains the menu item number. This extends the service capability for much better user interaction. A yellow pages service having a short name of yp expects two user parameters, category and area. Users invoke services by providing the values, for example, burger and home (a landmark for the user). The application searches for all the Burger stores in the home area. A returned message from the service result contains a name list of Burger stores. The user then issues another message to get detailed information about the stores in which he is interested.
A form is the result of a service invocation requesting user input. The ideal user interaction for ASK is to have the user fill in the input parameters on the command line instead of having to fill in the form, which requires more message round trips.
Figure 10-15, "Form capability" demonstrates the possible interaction of a phone book service. The phonedit command enables users to search and edit the phone number for a particular user. It expects a name as its parameter. jdoe is provided in the example. The information of jdoe is returned with a menu, enabling the device user to edit the phone number or remove the user. There are two options for editing the phone number:
Or
Since a session is maintained for each user, menu navigation is made possible. The term current menu identifies the latest menu a user received from the ASK. The state of the current menu is kept in the user session on ASK. Whenever a menu selection is made by a user, it applies to the current menu. If a menu has not yet been received for the user, the ASK will attempt to locate a service whose short name is the same as the number provided by the user. An error is returned when no such service is found.
A service invocation through short name or device address automatically cancels the menu state created by the previous service invocation. The figure below demonstrates the situation. A menu returns as a response to invoking the phonedit service. A message for requesting the stk service is subsequently issued. It clears the menu state created by the invocation of the phonedit service. An attempt to make a menu selection triggers an error message from the ASK.
A current form state is created in the user session whenever the user receives a form message. Subsequent form parameter values can be issued by the user to fill the parameter requested from the previous form message. If the user decides not to fill the form but to invoke another service, the Escape command can be issued to cancel the current form state. Once the form state is clear, any form parameters issued by the user are considered invalid. An error message should be returned in respond to a form parameter without a current form state.
Figure 10-17, "Current Form State" illustrates a form state example. The device user invokes the phonedit service without providing any parameters. A form message is returned to the user expecting the user to fill in the search name. If the device user changes his/her mind and decides to invoke another service (for example stk) the first step is to clear the form state so that ASK will not treat the command stk as the name value expected from the phonedit service. Then, a new stk command can be issued. These two steps are combined into one message by separating the two commands with the default command separator (;).
Multiple commands can be issued from one message. They can be issued from the same line, each command separated by the configurable command separator (default [;]). Or, commands can be on different lines. The first blank line or stop command (!s) encountered, marks the end of the command sequence. No command interpretation will be done on text after the mark.
Multiple parameters may be required for an Async service.The default parameter separator is a blank space. If a parameter value contains space within it, it can be enclosed by double quote to represent a single parameter value. The parameter separator is configurable at the service level.
The example below illustrates a direction service expecting both the from and destination (the to) addresses. The from address is provided with double quotes to enclose the whole value. The destination is supplied as a landmark, home from the user profile. The second message sent from the user is to request traffic information service. The service is configured to use a comma (,) as the parameter delimiter; users provide the parameter values with (,) to separate them.
The way to develop applications for ASK is basically the same as for the Device Portal. Service Provider receives user parameter from the device, and responds with the result (in Mobile XML format). The requirement on the ASK client is low; the ability to send and receive text messages. Therefore, only a subset of the Mobile XML tags are applicable to ASK, as shown in Table 10-6, "Summary of semantics for MobileXML tags".
Developers may choose to have a different logic flow (for example, rendering the results differently) for the ASK device. In this case, they would need to be able to recognize if the request was coming from an ASK device class. This is accomplished by checking the device class attribute of the user request. The request from ASK has the device class attribute value of either messenger, or micromessenger. The information can be acquired from the input arguments for a service written in adapter form, or the HTTP header for services based on HTTP/OC4J adapter. The input argument _DeviceCategory defined in the ServiceContext specifies the device class value for adapter formed services. For HTTP/OC4J based services, the value can be picked up through the HTTP header x-oracle-device.class.
Similarly, any section of the ASK specific Mobile XML result, created by the application, binds the value of messenger or micromessenger to the element attribute deviceclass. ASK processes elements common to all devices (with no value specified in deviceclass), or elements with the attributes containing the value of messenger or micromessenger.
All mobile XML service can be made ASK enabled from a technical standpoint. The user experience while using ASK is worth considering when deciding how to build an application or ASK enabling an existing application. This is the same practice you might have been applying to decide how you want to render you application to different types of devices (screen size, form factor and such). ASK assumes a client with plain text input so it is even more appropriate to consider user experience. Services that expect many user interactions or have a complicated UI may not work well.
In addition, some of the Mobile XML tags do not make sense for ASK and one should be aware of the specific semantics ASK has for the set of XML tags. Since ASK do not assume any sort of client side browsing capability, it is common that tags which assumes certain keys or actions on the device are not appropriate for ASK. The following table lists all the tags for MobileXML, as well as their semantics in the context of ASK.
This section is for advanced users.
Oracle9iAS Wireless runtime layer is a servlet in the OC4J servlet container. Oracle9iAS Wireless runtime processes requests from Hypertext Transaction Protocol (HTTP) user agents, async user agents (such as SMS, e-mail, two-way pagers), and autonomous mobile 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 Oracle9iAS Wireless.
The Oracle9iAS Wireless runtime tracks the runtime session independently of the Servlet session, by rewriting every URLs with an added parameter "PAsid," which specifies the session id. The session tracking identifies that a sequence of requests are submitted by the same device. The runtime session contains the user information, authentication contexts, adapter contexts, runtime contexts, URL caches, and other states essential for the context sensitive services.
WAP 2.0 devices that implement the WAP HTTP State Management Specification (http://www.wapforum.org/) can support cookies for session management. Most of the commercial WAP gateways manage persistent cookies on behalf of the devices. If the device or gateway does not support cookies, the OC4J servlet container falls back to URL rewriting for session tracking. Since the Oracle9iAS Wireless runtime also tracks the session, it is possible for more than one runtime session to be bound to a single servlet session. For example, two Netscape browsers on the same client PC can open two independent runtime sessions although the browsers share the same servlet session because of the shared cookie repository.
By default, the binding to the Servlet session is enabled and is necessary for the OC4J load balancing and fail over facility. The runtime session states are replicated to other OC4J instances in the "island" so that the device requests can be redirected to another OC4J instance in the island when the first instance fails. The runtime sessions that are bound to the servlet sessions are invalidated when the servlet sessions expire.
The session binding from the runtime session to Servlet session can be disabled by the parameter setting "enable.http.session.binding=false" in the System.properties file. Without binding to the servlet sessions, the runtime sessions are expired when the sessions remain idle for more than what is specified by the "wireless.session.expiration.time" parameter in the site configuration.
Every request from the device is serviced within the context of a valid runtime session. The requests from anonymous devices are also tracked and assigned to individual runtime sessions although the owners of the sessions are the same Guest user, which is an anonymous user. The Oracle9iAS Wireless runtime automatically provisions a "virtual" user in the Wireless repository for each device that can be consistently identified, using the identifiers available in the devices. Runtime sessions for virtual users are opened whenever the device identifiers are present in the requests. The device identifiers may be based on native device identifiers such as the Mobile Identification Number (MIN), Mobile Subscriber ISDN (MSISDN), Ipv6 Address, Electronic Serial Number (ESN), etc. The device identifiers may be also provisioned into the device by the WAP gateway. The WAP Client ID Specification (http://www.wapforum.org/) defines a standard scheme for supporting the device identifiers. If no device identifiers are supplied in the request, the Oracle9iAS Wireless runtime provisions the device identifiers into the devices using the persistent cookies whenever possible.
The Oracle9iAS Wireless runtime uses the device identifiers only to facilitate personalization under the virtual user. The runtime sessions opened under the virtual users have access to the information such as personalized presets and customization profiles in the repository. The device identifier also enables the device to reconnect to the same runtime session for the user, as long as the session has not expired. The device identifiers add robustness to the session management for Oracle9iAS Wireless, enabling continuity of the service in the face of intermittent connection losses. The users may also make telephone calls in between connections to the Oracle9iAS Wireless without losing their contexts.
Device identifiers are not a mean for authentication. Although the runtime sessions for the virtual users are not authenticated, it does not prevent the users from accessing their personalized portals. The users may establish authenticated sessions only if they register with the Oracle9iAS Wireless. The user can supply the user name and password during the registration. The user's personalization profiles and presets are still available to the user after the user becomes registered. The advantages of the registration include the authentication process that gives access to the secured services, such as the e-Wallets and financial transaction services.
The application programs for the services that require the authenticated sessions must add the "PAlogin=true" parameter in the URLs. When the Oracle9iAS Wireless runtime detects the PAlogin=true parameter among the URL parameters in the request for a service, the runtime tries to authenticate the user if the runtime session is not already authenticated. The authentication process, which typically involves the user supplying the user name and password to the Oracle9iAS Wireless Single Sign On (SSO) Server, is performed before the runtime invokes the service being requested. See Section 10.6.2.15, "User-Defined Hooks Examples" for how the folder renderer service can be used to prepare the URLs with "PAlogin" parameter for the secured services in a folder. After the "PAlogin" parameter invokes the authentication process, the application programs for secured services still have the responsibility to check that the session is authenticated. The applications that has direct access to the Oracle9iAS Wireless runtime objects can use isUserAuthenticated() method in oracle.panama.rt.Session interface. Applications written for the HttpAdapters can get the information from the Http header attribute "x-oracle-user.authkind" which has the values "authenticated" or "unauthenticated."
In addition, the applications can also check if the session is secured by the SSL, TLS, or WTLS channels. The application that has direct access to the Oracle9iAS Wireless runtime objects can use isSecure() method in the oracle.panama.rt.Request interface. Applications written for the HttpAdapters can get the isSecure() condition through the HTTP header attribute "x-oracle-device.secure," which has the values "true" or "false."
The authorization for access to a service is performed for each request for all authenticated or unauthenticated sessions. The authorization makes sure that the session user has the privilege to access the service. The default authorization policy does not differentiate whether the session is authenticated or unauthenticated. The unauthenticated sessions of a "virtual" or "registered" user has as much visibility as the authenticated sessions. It is therefore critical for the applications to apply the "PAlogin" parameter to enforce the authentication.
The Oracle9iAS Wireless runtime automatically provisions "virtual" users in the Wireless repository for the devices that can be consistently identified, using the identifiers available in the devices. The virtual user option gives the device owners immediate access to the personalization features of the portal, which enhance the user experience. It automates the provisioning process for the carrier and enterprise portal administrators using the emerging WAP Client ID standards.
The device owners can register with Oracle9iAS Wireless to gain access to secured services through authentication. The registration can be done from the setup menus by the device owner. This self-provisioning registration feature further simplifies the administration tasks. The devices with the virtual user support let the registered users connect to Oracle9iAS Wireless and access the personalized services without signing on to the system until they are requested by the secured services to authenticate. The virtual user feature not only improves the accessibility of the portal but also enhances the data mining capability of portal operators since the activities of the devices can be identified with virtual identities.
The virtual user feature can be disabled by the site wide configuration parameter setting "wireless.virtualuser.enabled=false." This property can be modified by the Enable Virtual User option in System Manager>Site>User Provisioning control panel. If the virtual user feature is disabled or if the device does not support device identifier, then the session is opened under the "Guest" user, which must be provisioned in the repository. The Oracle9iAS Wireless bootstrap repository includes the anonymous user "Guest."
Applications that have direct access to the Oracle9iAS runtime objects can check the value of oracle.panama.model.UserType returned by the getUserType() method in oracle.panama.model.User. The User of the runtime session can be retrieved from the getUser() method in oracle.panama.rt.Session. Applications that are written for the HttpAdapter can get the user type information from the HTTP header attribute "x-oracle-user.userkind." The possible values of this attribute are "anonymous," "virtual," or "registered."
During the request execution, the Oracle9iAS Wireless runtime dispatches the authentication, authorization, device identification, location acquisition, data logging and other business logics to the respective plug-in modules.
Oracle9iAS Wireless Runtime API provides the Java interfaces to examine the runtime execution states, monitor the runtime execution behavior, and augment the default execution semantics. The Runtime API consists of four Java packages:
These four packages are included in the wireless.jar file. Make sure you have included wireless.jar in your Java classpath when you compile your Java application or plug-in modules that depend on the Runtime API.
One set of the interfaces in the Runtime API, which is contained in the package oracle.panama.rt.hook, specifies the hooks that can be used by application developers for their customized plug-in modules. For example, the ListenerRegistrationHook registers listeners. Application developers can implement this hook interface for a customized listener registration module that lets the listeners selectively observe the event sources. A custom listener registration module may subscribe the listeners only to the requests for the billable services. Such a listener may add business rules to the runtime controllers.
The Runtime API consists of four public Java packages that provide interfaces for the following functions:
The new customized hook implementations, which implement the respective interfaces and the singleton pattern, are registered in the appropriate entries through System Manager>Site>Wireless Web Server>Hooks control panel in the Webtool.
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 classes in this package are:
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, Oracle9iAS Wireless runtime parses the URL query string to retrieve the parameters. The runtime agents or other internal clients can set these parameters programmatically. Since Oracle9iAS Wireless runtime may cache and rewrite the URL for HTTP user agents, some of the parameters are maintained in the URL cache for the user. Oracle9iAS Wireless runtime may have to parse both the query strings from the HTTP request and the URL cache to build a complete list of query parameters.
Step 2 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication" shows that each time a new request object is created, Oracle9iAS Wireless runtime passes the request object to the ListenerRegistrationHook to let the hook register listeners.
The following table 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 Mobile XML results can contain the runtime variables, (composed from the names of the parameters) by appending two underscore characters (__) before and after the parameter name. These runtime variables in the Mobile XML results are "place holders" which are replaced by the values of the parameters during the post processing phase (Step 25 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication") before the final result is returned to the requester.
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:
public void invoke(ServiceContext sc) { . Request request = sc.getRequest(); String value = request.getParameter(Request.REQUEST_LANDMARK); [4] request.setParameter(Request.SESSION_LANDMARK, "Redwood City"); [5] . }
This interface represents the Response objects in Oracle9iAS Wireless 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 Oracle9iAS Wireless runtime. A valid session is established after an anonymous user, virtual user, or registered user is identified for the session (refer to the Session Management Section above for the user identification process). Any request (or service invocation) can only be executed in a valid session context. A session can either expire after the session exceeds the maximum 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.
Step 7 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication" shows that each time a new session object is created, Oracle9iAS Wireless 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. A new ServiceContext object is created for each validated request. The ServiceContext stores the input parameters, output parameters, and Mobile XML results. The associated request and session can be accessed from the ServiceContext object.
The Mobile XML result can contain the system defined ServiceContext parameters using the runtime variables as "place holders," which are substituted with values during the post processing phase (Step 25 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication") before the final result is returned to the requester.
Runtime variables are composed from the names of the parameters, by appending two underscores (__) before and after the parameter name.
Example:
Mobile XML results can contain runtime variables as follows:
target="___REQUEST_NAME__?___SESSION__& PAoid=__PAoid__"
Given the variables above and if the following three conditions exist in the ServiceContext:
Then after the substitution of the runtime variables, the result becomes:
target="/ptg/rm?PAsid=ukAj6hH&PAoid=254"
All the input parameters, output parameters, and Mobile XML result in the ServiceContext are externalized as an XML document.
This XML document is the input document for the transformers. The XSLT stylesheets for the transformers must be written against the DTD for the ServiceContext's XML document.
The following table 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.
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 the above table. Line [7] shows that the parameters from the request object are also available among the ServiceContext arguments. However, the ServiceContext parameters are not part of the PARAMETERS attribute category in Request objects, and are not accessible from the Request objects. They can be accessed only from the ServiceContext arguments as shown in the following example.
Example:
public void invoke(ServiceContext sc) { . Arguments args = sc.getInputArguments(); . String language = args.getInputValue(ServiceContext.USER_LANGUAGE);[5] String encoding = args.getInputValue("Accept_encoding"); [6] String landmark = args.getInputValue(Request.REQUEST_LANDMARK); [7] }
The Java program constants 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 Mobile XML result. The "Arguments" tag is a sibling of the "Result" tag; it contains all input and output arguments.
The following example of the XML document for a ServiceContext shows the "ServiceRequest" tag as the root element of the ServiceContext. 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> <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> </SimpleResult> d. </Result> 2.</ServiceRequest>
In many situations, the customized hooks, listeners, and adapters require session-long, application-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 application-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 Oracle9iAS Wireless 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.
The SessionHolder class is defined in the oracle.panama.rt. package. The SessionHolder is the serializable representation of the runtime Session. It is used to bind the runtime Session to the servlet session as required for the OC4J cluster configuration. Only serializable objects placed in the runtime session and the servlet session are replicated among other OC4J instances in the island. The portal developers can get an instance of this serializable object using the getSessionHolder() method in the Session.
This case uses the ParmImpl servlet to illustrate the RequestFactory pattern. The following code example is the doGet() method of the ParmImpl servlet:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException { Request req = RequestFactory.createRequest(request, response); [3] if (req == null) { return; } try { Response resp = req.execute(); [8] } catch (Exception ex) { } finally { req.invalidate(); [11] } }
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 Request, thereby freeing all the resources associated with the request object.
The following case illustrates how a runtime agent uses the RequestFactory pattern to request services through the Oracle9iAS Wireless runtime.
import oracle.panama.rt.RequestFactory; import oracle.panama.rt.Request; import oracle.panama.rt.Response; import oracle.panama.rt.Session; import oracle.panama.rt.ServiceContext; . import oracle.panama.model.MetaLocator; import oracle.panama.model.ModelServices; import oracle.panama.model.Service; import oracle.panama.model.User; import oracle.panama.model.AlertAddress; . . . Session signon(String user, String password) throws PanamaException { Request request = RequestFactory.createRequest(user, password); request.validate();[17] Session session = request.getSession();[18] request.invalidate(); return session; } String invokeService(Session session, Service service, User user,
AlertAddress address, String symbol) { Request req; Response resp; ServiceContext sc; String content = null; try { req = RequestFactory.createRequest(session, service,
user, address);[28] if (req == null) { return null; } try { req.setParameter("TickerSymbol", symbol); sc = req.validate(); resp = req.execute(); if (sc.isAnyResultPresent()) { content = resp.getContent(); } } catch (Exception ex) { } finally { req.invalidate(); } return content; } } String userName = "orcladmin"; String password = "manager"; String effectiveUserName = "Guest"; String symbols[] = { "orcl", "sunw", "csco" }; void main() { ModelServices models = MetaLocator.getInstance().getModelServices(); User user = models.lookupUser(effectiveUserName); Service service = models.lookupService("YahooQuote"); AlertAddress[] addresses = user.getAddresses(); Session session = signon(userName, password); for (int i = 0; i < symbols.length(); i++) { . . String content = invokeService(session, service, user, addresses[0],
symbol[i]); . . } }
The signon() method signs on the user to the Oracle9iAS Wireless 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 Oracle9iAS Wireless 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 execution. 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 ListenerRegistrationHook subscribes the listeners to receive events from the subject, such as Request, Response, or Session.
The implementor of oracle.panama.rt.event.RequestListener can receive 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 10.6.2, "Reference Model". However, not all the Request-related events will be generated. Which specific Request-related event will be generated is controlled by the enent mask in System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel in the Webtool.
For example, if you want to have your RequestListener receive the request begin event, you should set the Enable 'request begin' Event to true in the System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel in the Webtool. The site configuration property names are:
wireless.http.event.beforeRequest wireless.http.event.requestBegin wireless.http.event.requestEnd wireless.http.event.serviceBegin wireless.http.event.serviceEnd wireless.http.event.transformBegin wireless.http.event.transformEnd wireless.http.event.requestError wireless.http.event.afterRequest
Step 11 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication" indicates that the RequestListener can intercept the input parameters during the requestBegin(RequestEvent) and apply additional business rules to the request parameters before service invocation.
The implementor of oracle.panama.rt.event.ResponseListener can receive the Response-related event. The only possible Response-related event is response error. If you want Oracle9iAS Wireless runtime to have your ResponseListener receive the response error event, you should set the Enable 'response error' Event option to true in System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel in the Webtool. The site configuration property name is: wireless.http.event.responseError
The implementor of oracle.panama.rt.event.SessionListener can receive the Session life cycle events. The possible Session events include:
The timing sequence regarding when the event is generated is discussed in Section 10.6.2, "Reference Model". However not all the Session events will be generated. Which specific Session event will be generated is controlled by the event masks in the System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel in the Webtool.
For example, if you want to have your SessionListener receive the session begin event, you should set the Enable 'session begin' Event option to true in the System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel in the Webtool. The site configuration property names are:
wireless.http.event.beforeSession wireless.http.event.sessionBegin wireless.http.event.sessionAuthenticated wireless.http.event.sessionEnd wireless.http.event.afterSession
The following guidelines describe how to set up the customized Event Listener:
wireless.http.locator.combined.listener.classes
wireless.http.locator.session.listener.classes
wireless.http.locator.response.listener.classes
wireless.http.locator.request.listener.classes
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)
sessionAuthenticated(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 10.6.1.16, "Implementing the RequestListener Interface". 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 Oracle9iAS Wireless runtime specifies the hook interfaces for standard plug-in modules. The following sections describe the hooks in the order in which they are invoked by the runtime.
In the Oracle9iAS Wireless Runtime API, Hook and Policy form a chain of responsibility design pattern, within which Policy is the default implementation of Hook that can be delegated by the custom implementation.
The following table lists the Hooks and the default Policies that correspond to the hook interfaces:
Steps 2 and 7 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication" show 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 billable services. Such a listener may add business rules to the runtime controller.
The Oracle9iAS Wireless 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.
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 request.
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 Oracle9iAS Wireless 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 in the Oracle9iAS Wireless Single Sign On (SSO) server.
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 Oracle9iAS Wireless 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.
In the Oracle9iAS Wireless environment, the sign on pages are generated by the SSO server. The SignOnPagesHook is used primarily for authentication against the stand alone repository. This hook is not shown in the execution because it is invoked only when the AuthenticationPolicy raises the AuthenticationFailOverException.
When the SignOnPagesHook generates the sign-on page, the Oracle9iAS Wireless runtime sends that sign-on page to the user, who submits the user's name and password for authentication by the default authentication module in stand alone mode (without SSO).
Runtime invokes the MobileIDHook to determine the mobile client ID.
The mobile identifier may be supplied by the external accounting system, by one of the fields (such as, the mobile ID field or external ID field) of the user object in the repository, or by one of the attributes in the HTTP header. The HTTP header attribute names can be specified by the "wireless.mobile.id.request.parameter.name" through the System Configuration Webtool. This mobile id is placed in 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 Oracle9iAS Wireless 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 Oracle9iAS Wireless provides two different default implementations of the CallerLocationHook interface.
The oracle.panama.rt.common.CallerLocator class provides the simple implementation using the location marks. The location object is one way of specifying the longitude and latitude position. The user can change the location setting in the session through the URL parameter PAslmk. If the automatic location acquisition is disabled, the location setting in the session supplies the current position of the mobile device to the location-based services.
The oracle.panama.rt.common.LocAcq provides the automatic location acquisition implementation if the user specifies the appropriate privacy directive. If the automatic acquisition fails or is disabled through the Enable Mobile Positioning flag in the System Manager -> Site -> Location Management control panel of the webtool or by setting the "wireless.elocation.mp.enable" parameter in the Site Configuration parameter table, the prior location mark 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 Oracle9iAS Wireless 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 two hooks control how the content of a folder gets rendered:
The FolderRendererHook uses the 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, accessible through the FolderRendererPolicy, combines the contents of the home folder with the folders and services from one or more of the user's groups. The LocationServiceVisibilityHook selects from the location-based subfolders in the folder for those whose regions intersect with the current position of the mobile device.
Each folder can be associated with a folder rendering service which provides customized view of the folder. The default folder rendering policy or the site wide customized folder rendering hook is used only if the folder does not have an associated rendering service, either assigned to it or inheritable from its parent folders.
If the PreProcessorHook is specified, the Runtime invokes the PreProcessorHook to process the Mobile XML result from the service invocation. The Device's Transformer is applied to the result of the PreProcessorHook. If specified, the PostProcessorHook is invoked to process the markup page that is generated by the Device's Transformer.
This section describes the Oracle9iAS Wireless runtime, 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 new request 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 starts the following sequence of activities within the runtime.
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.
findDeviceType(String)
Runtime dispatches to the DeviceIdentificationHook to determine the device model.
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.
getMobileId(Request,Session)
Runtime dispatches to the MobileIdHook to obtain the mobile 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.
sessionAuthenticated(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__.
process(Request,Response,String)
Runtime dispatches to the PostProcessorHook to process the device markup page to produce the final result.
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 Mobile XML results can contain the runtime variables, (composed from the names of the parameters) by appending two underscore characters (__) before and after the parameter name. These runtime variables in the Mobile XML results are "place holders" which are replaced by the values of the parameters during the post processing phase (Step 25 in Section 10.6.2.1, "Case: A Request Involving Session Establishment and Authentication") before the final result is returned to the requester. The following table 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. You can access them in one of two ways:
Arguments args = sc.getInputArguments(); String language = args.getInputValue(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 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:
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:
Table 10-11 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 wireless.jar file.
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 wireless.jar in your Java classpath during compilation.
Set the corresponding entry in the System Manager -> Site -> Wireless Web Server -> Event and Listeners control panel, or the System Manager -> Site -> Wireless Web Server -> Hooks control panel to specify the name of the class that provides the implementation.
The following table lists the property entry name in the System Manager -> Site -> Wireless Web Server -> Hooks control panel for each hook.
For example, if you provide your own implementation of the authentication hook, you should set the wireless.http.locator.authentication.hook.class in the System Manager -> Site -> Wireless Web Server -> Hooks control panel to <your class name>.
When implementing the new listeners, hooks, and adapters, consider also the following points:
The Oracle9iAS Wireless 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 Oracle9iAS Wireless 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 Oracle9iAS Wireless 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 Oracle9iAS Wireless runtime can be significantly degraded.
The following examples are available in the respective subdirectories under \sample.
The following examples illustrate how you can develop user-defined hooks:
The look and feel of folders in Oracle9iAS Wireless can be changed in the following ways:
The look and feel of the folder can be changed by modifying the following configuration parameters:
Oracle9iAS Wireless also allows an arbitrary service to be run when accessing a folder. This service is attached to the folder using the service designer; please see the service designer documentation for details. The service that renders the folder can either be active for that folder only, or for the given folder and all its children subfolders. The latter is useful for cases such as when one is customizing the folder look and feel for a subtree of folders. Customizing all user home folders is a prime example. If you put all user home folders beneath the folder /Users Home/, the FolderRendererService can then be attached to the /Users Home/ folder, with recursive rendering turned on (see the webtool documentation for details on how to do this). If you want to have different folder rendering for different groups of users, you should group the users home folder under different group folders and attach different folder rendering services to each group folder, like this:
A folder service is written just like any other Oracle9iAS Wireless service, and will get invoked with a regular ServiceContext. The folder to be rendered can be retrieved using the ServiceContext method getCurrentFolder.
The service used to render folders can be any Oracle9iAS Wireless service. It is usually convenient to write this service as a JSP, using the OC4J Adapter. In order to facilitate writing a FolderRenderer JSP service, the bean oracle.panama.rt.hook.FolderRendererBean is provided. This class has a number of methods for getting the content normally used by the built-in FolderRenderer: the getHeader, getBody and getFooter methods retrieve the header, body (folder content listing) and footer respectively. All methods in the FolderRenderBean takes a single argument, namely the current ServiceContext. In addition to the methods already mentioned, there are a number of utility methods (such as for getting the current user name), please see the FolderRendererBean JavaDoc for details.
The following example shows how write JSP code that displays a custom header, but reuses the built in folder renderer for displaying the folder content and footer:
<%@page import="oracle.panama.rt.ServiceContext"%> <%@page import="oracle.panama.rt.hook.FolderRendererBean" %> <% ServiceContext context = (ServiceContext) request.getAttribute("oracle.wireless.rt.context"); FolderRendererBean renderer = FolderRendererBean.getInstance(); response.setHeader("Mime-type", "text/xml"); %> <SimpleResult> <SimpleContainer> <SimpleText> <SimpleTextItem>My custom header</SimpleTextItem> </SimpleText> <%= renderer.getBody(context) %> <%= renderer.getFooter(context) %> </SimpleContainer> </SimpleResult>
The third way of customizing the folder is by specifying a hook class that implements the interface oracle.panama.rt.hook.FolderRendererHook. This hook has a single method invoke, which takes as its argument the current ServiceContext and returns the DOM document containing the Mobile XML for the current folder. The hook will be invoked whenever there is no assigned folder rendering service.
The default (built-in) implementation of the FolderRenderer is provided in the class oracle.panama.rt.hook.FolderRendererPolicy. This class can be subclassed, allowing custom hooks to reuse parts of the built-in functionality.
The main entry point for the FolderRendererHook is the invoke method. In the default FolderRendererPolicy implementation, the invoke method will create a SimpleResult element and in turn call getHeader, getBody and getFooter methods in order to append the header, body (folder content listing) and footer respectively. All methods in the FolderRendererPolicy takes a single argument, namely the current ServiceContext. If you need to add custom headers and footers, the Folder Renderer Policy can be subclassed to override the methods for getHeader and getFooter.
The following code is an example of a FolderRendererHook implementation that inserts a custom header:
import oracle.panama.rt.ServiceContext; import oracle.panama.rt.hook.FolderRendererHook; import oracle.panama.rt.hook.FolderRendererPolicy; import org.w3c.dom.Document; import org.w3c.dom.Element; class CustomFolderRenderer extends FolderRendererPolicy implements FolderRendererHook { public Element getHeader(ServiceContext context) { Document doc = context.getXMLDocument(); Element ret = doc.createElement("SimpleText"); Element text = doc.createElement("SimpleTextItem"); ret.appendChild(text); String str = "My custom header"; text.appendChild(doc.createTextNode(str)); return ret; } // inherit getBody // inherit getFooter }
The default folder renderer in the runtime puts the controls for setting up the end user's preferences in the header and footer. The actions that are added in the device header/footer is described by the FolderSetupAction interface. When writing a folder rendering service or hook, it is possible to get information about all actions, including the URL (String), the localized label and whether the action should be displayed or not. Please see the FolderRendererBean and FolderRendererPolicy JavaDoc for a complete list of methods that retrieves FolderSetupActions.
Using the FolderSetupActions allows the user that extends the FolderRenderer to duplicate the built-in setup button semantics and labels, but substitute their own look and feel, for example by using SimpleHrefs instead of SimpleMenuItems. The following code is an example of a FolderRendererHook that does this:
import oracle.panama.rt.ServiceContext; import oracle.panama.rt.hook.FolderRendererHook; import oracle.panama.rt.hook.FolderRendererPolicy; import oracle.panama.rt.hook.FolderSetupAction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; class CustomFolderRenderer extends FolderRendererPolicy implements FolderRendererHook { public Element getHeader(ServiceContext context) { Document doc = context.getXMLDocument(); Element ret = doc.createElement("SimpleText"); Element text = doc.createElement("SimpleTextItem"); ret.appendChild(text); FolderSetupAction[] actions = new FolderSetupAction[] { super.getEditPresetsAction(context), super.getEditServicesAction(context), super.getEditUserInfoAction(context), super.getLoginAction(context), super.getLogoffAction(context), super.getRegisterAction(context),
}; for(int i = 0; i < actions.length; i++) { if(actions[i].isActive(context)) { Element href = doc.createElement("SimpleHref"); // set the URL of the href href.setAttribute("target", actions[i].getURL(context)); // set the text to display for the href Text label = doc.createTextNode(actions[i].getLabel(context)) href.appendChild(label); text.appendChild(href); } } return ret; } // inherit getBody unchanged // override getFooter with implementation that creates footer // without setup buttons. public Element getFooter(ServiceContext context) { Document doc = context.getXMLDocument(); Element ret = doc.createElement("SimpleText"); Element text = doc.createElement("SimpleTextItem"); text.appendChild(doc.createTextNode("My custom footer")); return ret; } }
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 Oracle9iAS Wireless 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 add the name of the class, in this case hook.MyAuthenticator, in the System Manager > Site > Wireless Web Server > Hooks control panel in the Webtool under the wireless.http.locator.authentication.hook.class property.
The following partial example (the complete "runable" example is under the \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; 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 * @param an event */ 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())); event.getRequest().setAttribute(AttributeCategory.RUNTIME, SERVICE_END, new Long(event.getTimeStamp())); } /** * The event notification when transform begins * @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.
You should also add the name of the listener class, in this case listener.RequestListenerSample, in the System Manager > Site > Wireless Web Server > Event and Listeners control panel in the Webtool under the wireless.http.locator.request.listener.classes property.
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.
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; 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); } }
You should also add the name of the listener registration class, in this case listener.ListenerRegistrationHookSample, in the System Manager > Site > Wireless Web Server > Hooks control panel in the Webtool under the wireless.http.locator.listener.registration.hook.class property.
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 Manager -> Site -> Wireless Web Server -> Event and Listeners control panel of webtool.
The Oracle9iAS Wireless Repository comprises the models for the Model-View-Control (MVC) architecture, while the Oracle9iAS Wireless runtime layer comprises the controllers for the MVC. The repository Model API in oracle.panama.model package lets you develop applications that create, delete, modify, and query the persistent objects in the Oracle9iAS Wireless Repository. Developers of custom adapters and transformers can implement the corresponding Model interfaces to develop the applications that supply the business processes and contents for the Oracle9iAS Wireless portal. The developers can also implement the "controller" applications, through the adapter, listener, or hook components, that manipulate the repository objects to perform provisioning, registration, personalization, accounting, and similar type of functions.
The Oracle9iAS Wireless repository imposes the organizational structure among the objects. For example, a User can belong to multiple Group's. The User is assigned one or more Role's. The user can access the Service's that are accessible to the groups to which the user belongs. However, the implementations of the User interface can access external provisioning systems or repositories, such as the Oracle Internet Directory (OID) and the Oracle Applications User Repository (AOL), to manage the information for the enterprise users and specify the user's roles, the user's group membership, and the particular services that are accessible to the user.
A Folder is a special kind of Service used as a container of the services to build the service trees. A Service or Folder can be assigned to one or more groups. The User can own a collection of DeviceAddresses, a collection of LocationMark's, a collection of customization Profile's, and one or more collections of Presets' which are used in advanced personalization. A default LocationMark and a default Profile can be assigned for each User. The Device interface in the Model API defines the target device protocol (for example: WAP, SMS, or EMAIL), as well as specifies the physical characteristics of the target device that can be used by the adapters and the transformers (for example, screen width and height, screen columns and rows, and number of softkeys).
The intended users of the Model API are developers of customization portals, portlets, custom hooks, listeners, adapters, transformers, and applications such as JSPs, servlets, modules, and other (URL addressable) resources that are invoked through the HttpAdapter. Developers can also develop stand-alone applications which manipulate persistent objects using the Model API. Although these interfaces preserve the data integrity in the repository, they do not enforce access control security. The applications that access the repository through the Model API are not authenticated or authorized by the same Authentication and Authorization mechanisms in the Oracle9iAS Wireless runtime layer. In facts, the Model APIs are used by trusted components to develop and customize the authentication and authorization policies. The OracleMobile Online Studio, the System, Service, and Content Management Webtools, and the Customization Portals provide authentication and authorized access control to the repository. Developers should apply extreme caution when developing services using the interfaces in the Model API, and should take appropriate measures to prevent any undesired side effects when these services are invoked by the end users.
The repository objects are cached in the Java instances main memory when they are accessed from the Data Model API. These objects are removed from the main memory cache only after they are not accessed through the API for a time-to-live interval. This interval can be configured from "Cache Object Life Time" property in System Manager -> Site -> Runtime Configuration control panel in the webtool. If the repository object is modified and committed into the repository from one of the Java instances; all other Java instances will automatically reload the modified object from the repository. You can specify the number of cache synchronization threads from the System Manager -> Site -> Object Cache Synchronization control panel in the webtool.
The following sections describe the interfaces within the interface hierarchy in the Model API. These interfaces are contained in the oracle.panama.model
package. For a sample application that illustrates the use of some of the interfaces, see Section 10.6.4.1, "Sample Code". The oracle.panama.model package also provides the following three locator and factory objects to access the model objects.
MetaLocator, which is in the oracle.panama.model is used to access the ModelFactory and ModelServices.
ModelFactory, which is in the oracle.panama.model
package, provides the factory to create model objects.
ModelServices, which is in the oracle.panama.model
package, provides the locator or façade to access model objects.
The ModelObject is the root interface that represents the common behavior and properties of all repository objects. It is included in the oracle.panama.model
package. The figure below illustrates the inheritance hierarchy among all of the interfaces in the oracle.panama.model package.
The subinterfaces in the ModelObject interface hierarchy are all persistent objects. These subinterfaces are (in alphabetical order):
The following sections describe each subinterface.
Adapter extends the ModelObject interface. Adapter is the repository container for the RuntimeAdapter, which is the interface that is to be implemented by all custom adapters. The Adapter incorporates the RuntimeAdapter classes into the repository and supports the loading and initialization of the RuntimeAdapter.
Device extends the ModelObject interface. A Device is the definition of the target logical device protocol. It can, for example, be WML11 for WML specific devices, but also WML_Nokia7110 for Nokia specific WML. Other examples are SMS and EMAIL. Device contains the Transformer objects.
Observe that the same physical device can support multiple logical devices; a phone, for example, can support both the SMS and WAP protocols.
DeviceAddress extends the ModelObject interface. DeviceAddress contains the device-specific address, such as a phone or an email address. The DeviceAddress takes precedence over the AlertAddress, which is deprecated in this release.
Group extends the ModelObject interface. A Group is a collection of users. It is used to publish specific services to the group members. A user can access those services that are accessible to the group to which the user belongs.
LocationMark extends the ModelObject interface. It is a persistent object that represents the named and geocoded physical address.
The PresetCategory extends the ModelObject interface. PresetCategory defines the structure and attributes of the Presets. Each PresetCategory contains a collection of PresetDescriptors, which provides the meta information for the attributes in the Presets relation.
The PresetDescriptor extends the ModelObject interface. PresetDescriptor defines the meta data for an attribute in the Presets relation. The meta data of an attribute include the name, type, size, format, and description of the attribute.
The Presets interface extends the ModelObject interface. A Presets object contains a set of preset values whose structure and relation is defined by a PresetCategory. The Presets are owned by the User objects, and incorporates the personalized user preferences and frequently used input parameters for the services into the repository.
The Profile interface extends the ModelObject interface. The User can have one or more Profiles that encompass the user's customizations of the service trees. The Profile for a User can specify a preferred ordering of services in a folder.
Service extends the ModelObject interface. Service is an "abstract" interface and handles all generic aspects of a service.
It contains the following subinterfaces:
ExternalLink -- ExternalLink extends Service. An ExternalLink is a reference to an external URL.
Folder -- Folder extends the Service interface. A Folder is like a directory in a file system; it contains other services including other sub-folders.
Link -- Link extends the Service interface. A Link is a pointer to any other service "including" another Link. The Link is used to "customize" master services or to create private tree structures of accessible master services. It can override any accessible parameter kept by the service "chain" down to the final master service. Link contains the subinterface Alert.
Alert -- Alert extends the Link interface. An Alert (sometimes referred to as a Job) is a service which is set to be automatically executed, given a particular time interval specification. The Alert interface inherits methods from the following interfaces:
oracle.panama.model.Link
oracle.panama.model.Service
oracle.panama.model.ModelObject
MasterService -- MasterService extends the Service interface. The MasterService is the "final" Service. It is the template for all other Services. It always uses an Adapter to communicate with the external source.
Module - Module extends the Service interface. A Module is a pointer to a "modulable" service with well known name called "virtual" URL. The modules could be local or remote.
Module - Module extends the Module interface. A Module is a pointer to a "modulable" local MasterService. Local MasterService means that it is in the same repository as the Module.
Transformer extends the ModelObject interface. Transformer is the base interface for all transformation sub-classes. It is the repository container for the real transformation implementation (Java or XSL). It performs loading and initialization of the custom transformer classes that implements the oracle.panama.rt.xform.RtTransformer interface. It also provides the XSLT transformers for the XSLT stylesheets.
It has the following subinterfaces:
The User interface extends the ModelObject interface. The User represents the identity of the user and facilitate personalization in the Oracle9iAS Wireless portals.
Each user can be assigned a private root folder to contain the user's personal quicklinks. The user can access the services in the groups to which the user belongs. The implementation of the User interface may access external provisioning system or enterprise repositories such as Oracle Internet Directory (OID) to manage the information about the user.
The following sample code illustrates how you can provision new objects into the Oracle9iAS Wireless repository using the interfaces in the Model API. We choose the standalone class to introduce the sample codes, although other type of components, such as adapters, hooks, listeners, and servlets can be used to illustrate the Model API. The example only shows the search, create, delete, and commit operations in the Model API but does not include the necessary business logics.
The numbers that appear in brackets next to a line of code in the listing are referenced in the discussion to correlate the explanation with the corresponding lines in the code itself.
MetaLocator metaLocator = MetaLocator.getInstance(); modelFactory = metaLocator.getModelFactory(); modelServices = metaLocator.getModelServices();
The MetaLocator interface is used to lookup the ModelFactory and ModelServices. The getInstance() method in this interface gets the singleton instance of this MetaLocator. The methods getModelFactory and getModelServices look up the ModelFactory and the ModelServices.
Typically, to create a new object, you should check first if the object already exists. To look up any object, you use the ModelServices interface and the method lookupX(java.lang.String name), where X is the interface name of the object. In this sample code, to create a new user (the code section for creating a new user starts in line [2]), you first look up the user by using the lookupUser(userName) method in the ModelServices interface (line [3]), as the following line of code shows:
modelServices.lookupUser(userName);
Lookup operation should be the first step before creating any new persistent object in the Repository. The lookupUser(userName) method searches for the user by name and, if the User by that name is found, returns the User object. If the user with that name cannot be found, the method throws the PanamaRuntimeException.
Next, you check if the group to which the user belongs (or should belong) already exists (line [4]). Following the convention for looking up any object, you use the ModelServices interface and the lookupGroup(groupName) method to look up a group by name. If the group is found, the method returns the Group object. If the group is not found, the method throws the PanamaRuntimeException.
After checking if the user and the group already exist, you create the new user object (line [5] to line [6]):
{ user = modelFactory.createUser(userName, groups); } else { user = modelFactory.createUser(userName); } user.setPassword(userPassword); user.setEnabled(true);
You must save the newly created user. Each newly created object must be saved after it is created (line [7]):
modelFactory.save();
Save applies to all created or modified objects in the current thread. The objects are saved to the persistent storage and the transaction is committed. The method throws PanamaException if it is unable to save the work.
The searchUser() method in the sample code (line [8]) illustrates how to search a User object. To enumerate over a set of users (for example, all the users whose names start with the letter "B"), you use the ResultSetEnumeration (line [9]) returned by the method findUsers (line [10]). The method findUsers uses the pattern matching on the names. See also lines [11] and [12] in the listing of the complete sample code.
You should close the ResultSetEnumeration (line [13]) to release the database cursor, which otherwise will remain open.
To delete a User, you use the deleteUser method following the sample code section in line [14]. The user name must be exact in line [15]. ModelServices.lookupUser() method rejects the pattern matching templates by throwing exceptions. The user object is deleted in line [16].
import java.util.Vector; import oracle.panama.PanamaException; import oracle.panama.PanamaRuntimeException; import oracle.panama.model.MetaLocator; import oracle.panama.model.ModelFactory; import oracle.panama.model.ModelServices; import oracle.panama.model.ResultSetEnumeration; import oracle.panama.model.User; import oracle.panama.model.Group; /** * This is a sample program demonstrates the usage of the model API. */ public class SampleModelClient { private ModelFactory modelFactory; private ModelServices modelServices; public SampleModelClient() { MetaLocator metaLocator = MetaLocator.getInstance(); [1] modelFactory = metaLocator.getModelFactory(); modelServices = metaLocator.getModelServices(); } /** * Get all group names */ private String[] getGroupNames() throws PanamaException, PanamaRuntimeException { String[] names; ResultSetEnumeration result = null; try { // Find all user groups - use a wildcard for the name expression result = modelServices.findGroups("*"); Vector buffer = new Vector(); while (result.hasMoreElements()) { Group group = (Group)result.next(); String name = group.getName(); buffer.addElement(name); } names = new String[buffer.size()]; buffer.copyInto(names); } catch (PanamaRuntimeException ex) { throw ex; } finally { if (result != null) { result.close(); result = null; } } return names; } /** * Create a new user. */ private void createUser(String userName, String userPassword, String groupName) [2] throws PanamaException, PanamaRuntimeException { try { // First check if the user does not already exists modelServices.lookupUser(userName); [3] // If we are here the user must already exists return; } catch (PanamaRuntimeException ignore) {} Group group = null; try { // Get the group to add the user group = modelServices.lookupGroup(groupName); [4] } catch (PanamaRuntimeException ex) { // A PanamaRuntimeException is thrown if the group is not found group = null; } User user; // modelFactory.createUser() will automatically create a // home folder for the new user. if (group != null) { Group[] groups = new Group[1]; groups[0] = group; user = modelFactory.createUser(userName, groups); [5] } else { user = modelFactory.createUser(userName); } user.setPassword(userPassword); user.setEnabled(true); [6] // save the newly created object modelFactory.save(); [7] } /** * Search for users. */ private User[] searchUser(String userNamePattern) [8] throws PanamaException, PanamaRuntimeException { User[] users; ResultSetEnumeration result = null; [9] try { result = modelServices.findUsers(userNamePattern); [10] Vector buffer = new Vector(); while (result.hasMoreElements()) { [11] User user = (User) result.next(); [12] buffer.addElement(user); } users = new User[buffer.size()]; buffer.copyInto(users); } catch (PanamaRuntimeException ex) { throw ex; } finally { if (result != null) { result.close(); [13] result = null; } } return users; } /** * Delete a user. */ private void deleteUser(String userName) [14] throws PanamaException, PanamaRuntimeException { try { if (userName != null && userName.length() > 0) { User user = modelServices.lookupUser(userName); [15] user.delete(); [16] // Save the changes modelFactory.save(); } } catch (PanamaRuntimeException ex) { throw ex; } } }
Adapters are used to securely fetch application content and prepare it for device adaptation. Out-of-the-box, Oracle9iAS Wireless includes the HTTP Adapter. The HTTP Adapter is used to retrieve content from any HTTP/XML/J2EE server and application. The HTTP Adapter is compliant with HTTP 1.1. It supports HTTPS, cookies, and redirecting.
The method for creating mobile applications has been simplified in this release. Previously, it was common to create a Java Adapter for each mobile application. This would embed some of the application logic in an Adapter and some of the logic in the application itself. In order to leverage J2EE standards, the HTTP Adapter is recommended for mobile development. The complete mobile application can reside on any web server. The HTTP Adapter will point to the application URL to retrieve Oracle9iAS Wireless XML output. See the XML Developer's Guide section of this book for more information.
The HTTP Adapter fetches the Mobile XML content from the external HTTP/ HTTPS URLs. It acts as a proxy browser (which understands mobile xml) on behalf on the mobile device. Init Argument:
INVOKE LISTNER: This argument specifies the class path of the HTTP Adapter Listener. Refer to the javadoc of oracle.panama.adapter.http.event.HttpAdapterEventListener for more details on HttpAdapterEventListener Input Arguments.
Input Arguments:
The HTTP adapter supports all the standard browser features:
Example XML Document, showing the usage of relative and absolute URLs.
<?xml version = "1.0" encoding = "UTF-8" standalone="yes" ?> <!DOCTYPE SimpleResult PUBLIC "-//ORACLE//DTD SimpleResult 1.1.0//EN" "http://xmlns.oracle.com/ias/dtds/SimpleResult_1_1_0.dtd"> <SimpleResult> <SimpleContainer> <SimpleHref target="http://Oracle9iAS Wireless.oracle.com/HelloWorld.xml">Absolute URL</SimpleHref> <SimpleHref target="HelloWorld.xml">Relative URL </SimpleHref> </SimpleContainer> </SimpleResult>
The following jsp file sends a Post redirect to the URL http://Oracle9iAS Wireless.oracle.com. The param1=value1 is passed as post data to the URL
<% response.setHeader("x-oracle-mobile-redirect", "true"); response.setHeader("Content-Type", "tex/vnd.oracle.mobilexml"); %> <?xml version = "1.0" encoding = "UTF-8" standalone="yes" ?> <!DOCTYPE SimpleResult PUBLIC "-//ORACLE//DTD SimpleResult 1.1.0//EN" "http://xmlns.oracle.com/ias/dtds/SimpleResult_1_1_0.dtd"> <SimpleResult> <SimpleContainer> <SimpleForm name="ProcessSignOnForm" mimetype="text/vnd.oracle.mobilexml" target="http://Oracle9iAS Wireless.oracle.com/MyApp" method="post"> <SimpleFormItem name="param1" value="value1" type="hidden"/> </SimpleForm> </SimpleContainer> </SimpleResult>
The following mobile xml document shows the usage of the sendreferer attribute.
<?xml version="1.0" encoding="UTF-8"?> <SimpleResult> <SimpleContainer> <SimpleHref target="HelloWorld.xml" sendreferer="true">Send Referer</SimpleHref> <SimpleHref target="HelloWorld.xml" sendreferer="false">Don't Send Referer </SimpleHref> </SimpleContainer> </SimpleResult>
Following is the list of HTTP headers sent by the HTTP Headers.
The HTTP Adapter should be used to build mobile XML aware applications. The application can be built using any web programming technology like Java Server Pages (JSP), Servlet, Perl or Active Server Pages (ASP) and can be hosted on any web server. In Oracle9iAS Wireless 2.0 HTTP Adapter is the preferred way to build mobile xml applications.
The OC4J Adapter is used to fetch mobile xml content by invoking a JSP page in the same Java VM. The JSP page can access the request context information. The OC4J adapter is only for internal use of Oracle9iAS Wireless.
The Web Integration adapter retrieves and adapts Web content. The Web Integration adapter works with Web Interface Definition Language (WIDL) files to map source content to Portal-to-Go XML. Typically, the source format for the Web Integration adapter is HTML, but you can also use the adapter to retrieve content in other formats, such as XML. Portal-to-Go provides a visual tool for creating WIDL files, the Web Integration Developer. To create a WIDL file, you identify the elements of a Web page that you want to make accessible to a service. You then associate output and input parameters to the source elements that you want to access in a Portal-to-Go service.
The SQL Adapter allows service designers to create services based on SQL Statements on Stored Procedures. Any database with JDBC driver is supported. The SQL Adapter uses pool of database connections. The connection pool parameters can be specified as init arguments of the adapter.
Customers can implement their own adapters by implementing oracle.panama.adapter.RuntimeAdapter
interface (refer to javadoc). In this section we will implement a simple RMIAdapter, which fetches mobile xml content by invoking RMI methods.
Lets look at the implementation of the adapter
package oracle.panama.adapter.rmi; import java.io.StringReader; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.lang.reflect.Method; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.NotBoundException; import org.w3c.dom.Element; import org.w3c.dom.Document; import oracle.panama.PAPrimitive; import oracle.panama.Argument; import oracle.panama.Arguments; import oracle.panama.ArgumentType; import oracle.panama.OutputArguments; import oracle.panama.adapter.RuntimeAdapter; import oracle.panama.adapter.RuntimeAdapterHelper; import oracle.panama.adapter.AdapterException; import oracle.panama.rt.ServiceContext; import oracle.panama.core.xml.XML; /** * A Simple RMI Adapter - invokes RMI methods to fetch mobile xml content */
All the adapters implement RuntimeAdapter interface
public class RMIAdapter implements RuntimeAdapter { // init arguments private Arguments initArgs = null; // input arguments private Arguments inputArgs = null; // output arguments private OutputArguments outputArgs = null; private boolean initialized = false; // reference to remote object private Object remoteObject = null; // remote interface private String remoteInterface; // hash table containing the method name to Method object mapping private Hashtable accessibleMethods = null; // Init argument - specifies the rmi url of the remote object private static final String RMI_OBJECT_URL = "RMI_OBJECT_URL"; // Init argument - specifies the remote interface private static final String REMOTE_INTERFACE = "REMOTE_INTERFACE"; // Input argument - specifies the remote method name to invoke private static final String METHOD_NAME = "METHOD_NAME";
The getInitArguments() method returns the init arguments required to initialize the adapter. The values of these arguments are specified during the creation of Master Service. The UI tools like Service Designer use this method of display the list of init that are required for creating a master service.
The RMI Adapter has following init arguments
/** * Get the init arguments * @return init arguments */ public Arguments getInitArguments() throws AdapterException { if (initArgs == null) { synchronized (this) { if (initArgs == null) { initArgs = RuntimeAdapterHelper.createArguments(); Argument arg = null; arg = initArgs.createInput(RMI_OBJECT_URL); arg.setComment("The RMI OBJECT URL for eg., rmi://rmiserver.com:2008/HelloWorld"); arg.setType(ArgumentType.SINGLE_LINE); arg.setCaption("RMI Server URL"); arg = initArgs.createInput(REMOTE_INTERFACE); arg.setComment("The Remote Interface"); arg.setType(ArgumentType.SINGLE_LINE); arg.setCaption("Remote Interface"); } } } return initArgs; }
This method returns the Input Arguments expected by the Adapter.
/** * Get the input Arguments * @return input arguments */ public Arguments getInputArguments() throws AdapterException { return inputArgs; }
This method returns the Output Arguments
/** * Get the output Arguments * @return an array of output arguments */ public OutputArguments getOutputArguments() throws AdapterException { return outputArgs; }
The init method initializes the adapter. The init adapter of the method is called once, when the master service pointing to the adapter is invoked or the getMergedInputArguments() method of the MasterService is called. The content of the init method must be synchronized to ensure that the class is not initialized by another thread.
The init method of RMIAdapter does the following
/** * Initialize the adapter using the information from the init arguments. * @param args init arguments */ public void init(Arguments args) throws AdapterException { if (initialized == false) { synchronized (this) { if (initialized == false) { String rmiObjectUrl = args.getInputValue(RMI_OBJECT_URL); remoteInterface = args.getInputValue(REMOTE_INTERFACE); // check if both the init args are specified if ((rmiObjectUrl == null) || (rmiObjectUrl.equals("")) || (remoteInterface == null) || (remoteInterface.equals(""))) { throw new AdapterException("Init parameters missing"); } // Get reference to remote object Class interfaceClass = null; try { remoteObject = Naming.lookup(rmiObjectUrl); interfaceClass = Class.forName(remoteInterface); } catch (Exception ex) { throw new AdapterException(ex); } Method[] methods = interfaceClass.getMethods(); accessibleMethods = new Hashtable(); for (int i=0; i<methods.length; i++) { if (Modifier.isPublic(methods[i].getModifiers())) { accessibleMethods.put(methods[i].getName(), methods[i]); } } // Create Input Arguments inputArgs = RuntimeAdapterHelper.createArguments(); Argument arg = inputArgs.createInput(METHOD_NAME); arg.setType(ArgumentType.ENUM); String[] accessibleMethodNames = getAccessibleMethodNames(); arg.setOptions(accessibleMethodNames); // Create Output Arguments outputArgs = RuntimeAdapterHelper.createOutputArguments(); initialized = true; } } } }
The method returns an array of accessible method names
// returns the array of accessible method names private String[] getAccessibleMethodNames() { Enumeration enum = accessibleMethods.keys(); Vector v = new Vector(); while (enum.hasMoreElements()) { String methodName = (String) enum.nextElement(); v.add(methodName); } String[] methodNames = new String[v.size()]; methodNames = (String []) v.toArray(methodNames); return methodNames; }
The invoke method is called when a client invokes a master service pointing to this adapter. The method executes the client request and returns the mobile xml result to the master service.
The method takes one argument of type ServiceContext. For each end user request received by the Oracle9iAS Wireless Server a ServiceContext object is created. The ServiceContext object contains all the user input arguments and arguments specified in Alias and Master Service.
/** * Invoke the adapter using the input and output parameters in the * service context. * @param serviceContext the context that contains input parameters */ public Element invoke(ServiceContext serviceContext) throws AdapterException { checkState(); // Get the input argument metod name String methodName = serviceContext.getInputArguments().getInputValue(METHOD_NAME);
If the method name is not specified the mobile xml displaying the list of available methods as menu items is returned
if ((methodName == null) || "".equals(methodName)) { return getMethodMenuElement(serviceContext); } else {
The specified method is executed.
return invokeRemoteMethod(methodName); } }
The destroy() method releases the resources acquired by the adapter in the init method.
/** * Destroy the provider. */ public void destroy() { remoteObject = null; }
Utility method to check if the adapter is initialized.
private void checkState() throws AdapterException { if (initialized == false) { throw new AdapterException("Adapter is not initialized"); } }
The invokeRemoteMethod() method invokes the remote method and converts the returned String to an XML DOM Element.
private Element invokeRemoteMethod( String methodName) throws AdapterException{ Method method = (Method) accessibleMethods.get(methodName); if (method == null) { throw new AdapterException("method "+ methodName + " is not available"); } try { // invoke the remote method String retString = (String) method.invoke(remoteObject, new Object[0]); Element elt = XML.makeElement(new StringReader(retString)); return elt; } catch (Exception ex) { throw new AdapterException(ex); } }
The getMethodMenuElement() method returns mobile xml element for displaying available methods as menu items.
private Element getMethodMenuElement(ServiceContext serviceContext) { // Returns SimpleMenu containing method names Document doc = serviceContext.getXMLDocument(); Element simpleResultElt = PAPrimitive.createSimpleResult(doc, null); Element simpleContainerElt = PAPrimitive.createSimpleContainer(doc, "MethodMenu"); simpleResultElt.appendChild(simpleContainerElt); Element simpleMenuElt = PAPrimitive.createSimpleMenu(doc, "SimpleMenu"); simpleContainerElt.appendChild(simpleMenuElt); Enumeration enum = accessibleMethods.keys(); while (enum.hasMoreElements()) { String methodName = (String) enum.nextElement(); Method m = (Method) accessibleMethods.get(methodName); String target = RuntimeAdapterHelper.getURLPAoidParameter(serviceContext.getInputArguments()); target += "&" + METHOD_NAME + "=" + m.getName(); Element simpleMenuItemElt = PAPrimitive.createSimpleMenuItem(doc, m.getName(), target, false); simpleMenuElt.appendChild(simpleMenuItemElt); } return simpleResultElt; } }
The following sample RMI implementation can be used to test the RMI Adapter
SampleInterface.java: Remote Interface
package oracle.panama.adapter.rmi; import java.rmi.Remote; import java.rmi.RemoteException; public interface SampleInterface extends Remote { // Returns Hello World message String sayHelloWorld() throws RemoteException; // Returns the current time String getTime() throws RemoteException; }
SampleImpl.java: Remote Implementation
package oracle.panama.adapter.rmi; import java.io.*; import java.util.Calendar; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import java.rmi.registry.LocateRegistry; import org.w3c.dom.*; import oracle.xml.parser.v2.*; public class SampleImpl extends UnicastRemoteObject implements SampleInterface { public final static int RMI_REGISTRY_PORT = 2099; public SampleImpl() throws RemoteException { super(); } public String sayHelloWorld() { return createMobileXMLMessageString("Hello World!"); } public String getTime() { return createMobileXMLMessageString(Calendar.getInstance().getTime().toString()); } String createMobileXMLMessageString(String message) { StringBuffer buf = new StringBuffer(1024); buf.append("<?xml version = \"1.0\" encoding = \"UTF-8\" standalone=\"yes\" ?>"); buf.append("<!DOCTYPE SimpleResult PUBLIC \"-//ORACLE//DTD SimpleResult 1.1.0//EN\" \"http://xmlns.oracle.com/ias/dtds/SimpleResult_1_1_0.dtd\">"); buf.append("<SimpleResult>"); buf.append("<SimpleContainer>"); buf.append("<SimpleText>"); buf.append("<SimpleTextItem>"); buf.append(message); buf.append("</SimpleTextItem>"); buf.append("</SimpleText>"); buf.append("</SimpleContainer>"); buf.append("</SimpleResult>"); return buf.toString(); } public static void main(String args[]) { // Create and install a security manager if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { // Create Registry LocateRegistry.createRegistry(RMI_REGISTRY_PORT); SampleImpl obj = new SampleImpl(); // Bind this object instance to the name "HelloServer" Naming.rebind("//localhost:"+ RMI_REGISTRY_PORT+ "/Sample", obj); System.out.println("Sample bound in registry"); } catch (Exception e) { System.out.println("Sample err: " + e.getMessage()); e.printStackTrace(); } } }
Steps involved in testing the RMI Adapter
|
Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|