Skip Headers

Oracle9i Case Studies - XML Applications
Release 1 (9.0.1)

Part Number A88895-01
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

5
Customizing Content with XML: Dynamic News Application

This chapter contains the following sections:

Introduction to the Dynamic News Application

The Dynamic News application uses Oracle XML platform components together with the Oracle9i database to build a web-based news service. It combines Java, XML, XSL, HTML, and Oracle9i.

Dynamic News Main Tasks

Dynamic News application shows you how to do the following tasks:

Overview of the Dynamic News Application

Dynamic News pulls news items (headlines) from the database to build HTML pages. The HTML pages are customized according to user preferences.

The pages present lists of items, with each item hyperlinked to a complete article. Each news item has attributes including:

Three Levels of Customization: Static, Semi-Dynamic, and Dynamic

Dynamic News uses these attributes to offer three levels of customization:

Table 5-1 describes these usage choices.


Table 5-1 Dynamic News: Three Levels of Customization
Customization Level  Description 

Static 

Static pages are not customized.

An end-user at this level gets a page listing all items from each category, sub-category, and type.

The news system administrator uses the Administration servlet to generate static XML documents periodically (for example, every hour on the hour).

The application could build such pages on demand, but it's faster to serve up a pregenerated page than to run a query and build the same page for each user who requests it.  

Semi-Dynamic 

Semi-dynamic pages combine pregenerated lists of items.

An end-user chooses one or more categories, and Dynamic News builds a page listing the items from those categories. The news admin uses the Administration servlet periodically to pregenerate the lists of items in each category.

Like static pages, semi-dynamic pages are built from pregenerated documents to improve performance.  

Dynamic 

Dynamic pages are built when end-users request them. Content comes directly from the database; nothing is pregenerated.

First, an end-user invokes a servlet to choose categories, sub-categories, and types. Next, Dynamic News queries the database for items matching that criteria and uses the result set to build an XML document. Then, as with static and semi-dynamic pages, it applies an XSLT transformation to generate HTML.  


Note:

The term "dynamic" and "static" refer to the page contents not its behavior.  


Dynamic News SQL Example 1: Item Schema, nisetup.sql

Here's the SQL from nisetup.sql, that defines the structure of a news item:

CREATE TABLE news.NEWS_ITEMS 
( ID   NUMBER NOT NULL, 
  TITLE           VARCHAR2(200), 
  URL             VARCHAR2(200),  
  DESCRIPTION     VARCHAR2(2000),  
  ENTRY_DATE      DATE,  
  CATEGORY_ID     NUMBER,  
  SUB_CATEGORY_ID NUMBER,  
  TYPE_ID         NUMBER,  
  SUBMITTED_BY_ID NUMBER,  
  EXPIRATION_DATE DATE,  A
  APPROVED_FLAG   VARCHAR2(1)
);

Dynamic News Servlets

Table 5-2 lists the servlets used in the Dynamic News application. These servlets provide entry points to the application logic:


Table 5-2 Dynamic News Servlets
Servlet  Description  File Name 

Administration 

  • Adds news items to the database.

  • Maintains lists of users, types, and categories.

  • Generates XML and HTML for a static (non-customized) news page.

 

xmlnews/admin/AdminServlet.java 

Semi-Dynamic 

Generates lists of news items in categories chosen by the end-user. 

xmlnews/dynamic/SemiDynamicServlet.java 

Dynamic 

Retrieves news items from the database to generate custom pages based on end-user preferences. 

xmlnews/dynamic/DynamicServlet.java 

How Dynamic News Works: Bird's Eye View

Generating XML Documents to Build HTML Pages

Dynamic News generates XML documents to build HTML pages:

Figure 5-1 gives an overview of how Dynamic News performs these steps:

  1. Calls Oracle XML SQL Utility (XSU). This queries the database for news items and writes the results to an XML document. This happens as follows:

    • In batch mode for Static pages

    • In batch mode for Semi-Dynamic pages

    • On demand for Dynamic pages

  2. Uses the XSL-T Processor of the Oracle XML Parser for Java to transform the XML into HTML via one of three XSL stylesheets: one for Netscape Navigator, one for Internet Explorer, or a general stylesheet for all other browsers.

  3. Delivers the HTML page to the user through a Web server.

Figure 5-1 Dynamic News


Text description of adxml081.gif follows
Text description of the illustration adxml081.gif

Static Pages

Dynamic News generates static pages to display all available news items. These pages are built at intervals set by the news system administrator, for example, every hour on the hour; otherwise, they don't change.

When to Use Static Pages?

Static pages are useful in any application where data doesn't change very often. For example, when publishing daily summaries from ERP or customer applications. Because the content is static, it's more efficient to pregenerate a page than to build one for each user who requests it.

How Static Pages Works

The admin executes a batch process, implemented from the Administration servlet, that queries the database and generates an XML document. When an end-user invokes Dynamic News to display all news, a servlet gets the browser type from the user-agent header of the HTTP request, then reads the XML document, and applies the appropriate XSL stylesheet.

Finally, it returns an HTML page formatted for the end-user's browser, as shown in Figure 5-2.

Another approach would be to apply XSL stylesheets as part of the batch process, generating one HTML file for each stylesheet. In this case, you end up with more files to manage, but the runtime servlet is smaller.

Figure 5-2 Dynamic News: Static Pages - Generating XML Documents


Text description of adxml073.gif follows
Text description of the illustration adxml073.gif

Semi-Dynamic Pages

The application builds semi-dynamic pages by combining pregenerated lists. The lists of items per category are pregenerated by the administrator (one XML file for each category), but pages that contain them are customized for each user. End-users choose categories such as Sports, Business, and Entertainment.

When to Use Semi-Dynamic Pages

The semi-dynamic approach is useful when the data doesn't change very often and you want to give the end-user a relatively small number of choices. An application that offers more choices has to pregenerate more documents, and benefits degrade proportionally.

How Semi-Dynamic Pages Work

Figure 5-3 shows how semi-dynamic generation works. There are two phases:

Figure 5-3 Dynamic News: Semi-Dynamic Pages - Generating XML Documents


Text description of adxml026.gif follows
Text description of the illustration adxml026.gif

Dynamic Pages

The application builds dynamic pages on demand by pulling items directly from the database. End-users access the "Create/Edit User Preference Page" to choose categories, subcategories, and types (for example, Entertainment - Movies - Review).

When to Use Dynamic Pages

Dynamic pages are useful for delivering up-to-the-minute information, such as breaking news. They are also useful for delivering historical data, such as the closing price of any specified stock on any day in the last 10 years. It would be impractical to pregenerate documents for every possible request, but straightforward and efficient to pull the figures from the database.

How Dynamic Pages Works

Figure 5-4 shows how dynamic generation works. Unlike the other runtime models, the administrator does not pregenerate XML documents. Instead, the Dynamic Servlet queries the database for news items based on the end-user's customization choices.

The servlet stores user preferences both in the database and in a client-side cookie, and reads them from the cookie where possible to improve performance. Using the query results, the servlet generates an XML file and transforms it using an XSL stylesheet into an HTML page for the user's browser. As with the other approaches, the application gets the browser type from the user-agent header of the HTTP request.

Figure 5-4 Dynamic News: Dynamic Pages - Generating XML Documents


Text description of adxml027.gif follows
Text description of the illustration adxml027.gif

Personalizing Content

Oracle9i makes Dynamic News flexible. Because news items are stored in the database, Dynamic News can customize content on demand. The code examples in this section show how the application personalizes pages by retrieving news items in categories specified by the end-user. The main tasks are:

  1. Get end-user preferences.

  2. Pull news items from the database.

  3. Combine news items to build a document.

  4. After assembling personalized content, the application customizes presentation of the page, formatting it for the end-user's browser as described later in this document.

1 Get End-User Preferences

Logic for processing preferences is distributed throughout the application, which stores the data both in the database and in client-side cookies. The application reads preference data from a cookie whenever possible to improve performance. If it can't get the data from a cookie (for example, because the end-user is visiting the site for the first time, or the end-user's browser does not accept cookies), the application reads preference data from the database.

From a Client-Side Cookie

The two methods below show how the application processes preference data stored in a cookie. Both methods come from xmlnews.common.UserPreference. Here's a sample cookie:

DynamicServlet=3$0$0#4$2$1***242

The cookie uses dollar signs to separate preference values, pound signs to separate categories, and three asterisks as a token to separate user ID and preference data. The sample cookie above shows that user 242 wants items from categories 3 and 4. In category 3, the user wants items of all types in all subcategories (a value of 0 selects all items). In category 4, the user wants items from subcategory 2 only, and within that subcategory, only items of type 1.

The sample application processes such cookies in two steps:

  1. First, getNewsCookie gets the "DynamicServlet" cookie from the browser that issued the HTTP request.

  2. Then loadPreferenceFromCookie parses it to get a String that contains that user's ID and preferences.

    public Cookie getNewsCookie(HttpServletRequest request)
    
                      throws Exception {
              Cookie c[] = request.getCookies();
              Cookie l_returnCookie = null;
                      for (int i = 0; (c!= null) && (i < c.length); i++) {
                      if (c[i].getName().equals("DynamicServlet")) {
                          l_returnCookie = c[i];
                          }
                        }
                        return l_returnCookie;
                      }
        public Vector loadPreferenceFromCookie(Cookie p_cookie) throws Exception {
              Vector l_prefId = new Vector(2);    
              String l_Preferences = p_cookie.getValue();
              StringTokenizer l_stToken = new StringTokenizer(l_Preferences, "***");
              String l_userId = "";
              while (l_stToken.hasMoreTokens()) {
                 // First Token is User Preference.
                 l_Preferences = l_stToken.nextToken();
                 // Second Token is User ID.
                 l_userId = l_stToken.nextToken();
              }
              l_prefId.addElement(l_Preferences);
              l_prefId.addElement(l_userId);
              return l_prefId;
           }
    

Querying the Database

If it can't read preferences from a cookie, the application queries the database. The class xmlnews.common.GenUtility implements methods that connect to the database and fetch news categories, sub-categories, and types.

The semi-dynamic servlet and the dynamic servlet both call these methods and the methods loadInitalPreference and constructUserPreference . These are both implemented in xmlnews/common/UserPreference.java.

Method loadInitalPreference calls getSubCategories, then loops through the result set, combining category values with separator characters to build a preference string.

public String loadInitialPreference(Vector p_category, Vector p_subcategory,
        Vector p_types, Connection p_con)
throws Exception {
GenUtility m_general = new GenUtility();
...
for (int i = 0; i < p_category.size(); i++) {
        String l_cat[] = (String []) p_category.elementAt(i);
        l_category = l_cat[0];
        Vector l_subcategory = m_general.getSubCategories(p_con,l_cat[0]);

        for(int l_j = 0, l_k = 0; l_j < l_subcategory.size(); l_j++, l_k++)
 {
...     
// Append the next preferences to the constructed string
   l_userPref = l_userPref+"#"+l_category+"$"+l_subCat+"$"+l_typeStr;
          }
   }   
...
    return l_userPref;
   }

public static Vector getSubCategories(Connection p_conn, String p_categoryId)
    throws Exception {
    Vector l_subCats = new Vector();

PreparedStatement l_pstmt = p_conn.prepareStatement(
   "Select id, name from sub_categories where category_id = ? ");
    l_pstmt.setString(1, p_categoryId);
    ResultSet l_rset = l_pstmt.executeQuery();

while (l_rset.next()) {
     String[] l_subCat = new String[2];
     l_subCat[0] = new String(l_rset.getString(1));
     l_subCat[1] = new String(l_rset.getString(2));
     l_subCats.addElement(l_subCat);
     }
l_pstmt.close(); 
return l_subCats; 
}

For example, the following code comes from xmlnews.dynamic.DynamicServlet.service.

It calls these methods to read end-user preferences from the database, then uses the preferences to build an HTML page.

public void service(HttpServletRequest p_request, 
      HttpServletResponse p_  response)
      throws ServletException {

   // The following are declared elsewhere as class variables 
   // and initialized in the servlet's init method.
   // GenUtility m_general = null; 

   // m_general = new GenUtility();
   // UserPreference m_userPreference = null;
   // m_userPreference = new UserPreference();
   ...

   // If the database connection has been closed, reopen it.
      if (m_connection == null || m_connection.isClosed())
          m_connection = m_general.dbConnection();
   ...  
      String l_preference = m_userPreference.loadInitialPreference(
      m_general.getCategories(m_connection),
      null, m_general.getTypes(m_connection),
                               m_connection);

m_userPreference = m_userPreference.constructUserPreference
   ( l_preference,m_status);

   // Display the Dynamic Page
      this.sendDynamicPage(l_browserType, p_response, 
      l_userName, m_userPreference,
      m_servletPath + "?REQUEST_TYPE=SET_ADVANCED_USER_PREFS",
      m_servletPath + "?REQUEST_TYPE=LOGIN_REQUEST",
      m_servletPath + "?REQUEST_TYPE=LOG_OUT_REQUEST",
      m_servletPath); 
    ...
 }

2 Pull News Items from the Database

The following code, from xmlnews.admin.AdminServlet.performGeneration and xmlnews.admin.AdminServlet.staticProcessingHtml, shows how the application queries the database for news items in each available category and converts each result set to a XML document.

The database stores the XML for each category as a CLOB (Character Large OBject), so the application can handle very long lists.

public void performGeneration(String p_user, String p_genType,
      HttpServletResponse p_response)
      throws ServletException, IOException {
...
      try {
      String l_fileSep = System.getProperty("file.separator");
      String l_message = ""; // Holds status message

      if (p_genType.equals("BATCH_GEN")) { // Batch Generation
      String l_htmlFile = "BatchGeneration";
      String l_xslFile  = "BatchGeneration";
      String l_xmlFile  = "BatchGeneration";

      // Generate the XML and HTML content and save it in a file
         this.staticProcessingHtml(
         m_dynNewsEnv.m_dynNewsHome+l_fileSep+l_htmlFile+".html",
         m_dynNewsEnv.m_dynNewsHome+l_fileSep+m_dynNewsEnv.m_batchGenXSL,
         m_dynNewsEnv.m_dynNewsHome+l_fileSep+l_xmlFile+".xml"
      );
      ...
    }
 ...
 }

The method xmlnews.admin.AdminServlet.staticProcessingHtml defines and executes a query to fetch the news items. Then it uses the Oracle XML SQL Utility (XSU) to build an XML document from the result set and create an HTML page by applying an XSLT transformation.

public void staticProcessingHtml(String p_htmlFile,String p_xslfile,
   String p_xmlfile) throws Exception {
   String l_query = "select a.id, a.title, a.URL, a.DESCRIPTION, " +
   " to_char(a.ENTRY_DATE, 'DD-MON-YYYY'), a.CATEGORY_ID, b.name, 
                   a.SUB_CATEGORY_ID, c.name, a.Type_Id, d.name, " +
" a.Submitted_By_Id, e.name, to_char(a.expiration_date, 'DD-MON-YYYY'),
                                                            a.approved_flag " +
" from news_items a, categories b, sub_categories c, types d, users e where " +
" a.category_id is not null and a.sub_category_id is not null and "+
" a.type_id is not null and a.EXPIRATION_DATE is not null and "+
" a.category_id = b.id  AND a.SUB_CATEGORY_ID = c.id AND a.Type_ID = d.id 
                          AND " +
" a.SUBMITTED_BY_ID = e.id AND "+
" a.EXPIRATION_DATE > SYSDATE AND "+
" a.APPROVED_FLAG = \'A\' ORDER BY b.name, c.name ";

 Statement l_stmt = m_connection.createStatement();
 ResultSet l_result = l_stmt.executeQuery(l_query);
// Construct the XML Document using Oracle XML SQL Utility
   XMLDocument l_xmlDocument = m_xmlHandler.constructXMLDoc(l_result);
   l_stmt.close();

// Get the HTML String by applying corresponding XSL to XML.
   String l_htmlString = m_xmlHandler.applyXSLtoXML(l_xmlDocument,p_xslfile);

 File l_file = new File(p_htmlFile);
 FileOutputStream l_fileout = new FileOutputStream(l_file);
 FileOutputStream l_xmlfileout = new FileOutputStream(new File(p_xmlfile));.
 l_fileout.write(l_htmlString.getBytes());
 l_xmlDocument.print(l_xmlfileout);

 l_fileout.close();
 l_xmlfileout.close();
}

3 Combine News Items to Build a Document

The final step in personalizing content is converting XML documents into HTML pages according to end-user preferences.

The following code comes from xmlnews.generation.SemiDynamicGenerate.dynamicProcessing.

It retrieves the CLOBs corresponding to categories chosen by the user, converts each CLOB to an XML document, then combines them into one XML document. The process of converting the XML document to an HTML page is described in the next section.

public XMLDocument semiDynamicProcessingXML(Connection p_conn, UserPreference p_
prefs)
        throws Exception
 {
     String l_htmlString = null ;
     XMLDocument l_combinedXMLDocument = null ;
     XMLDocument[] l_XMLArray = new XMLDocument[p_prefs.m_categories.size()];
     int l_arrayIndex = 0 ;

     PreparedStatement l_selectStmt = p_conn.prepareStatement(
                  " SELECT PREGEN_XML FROM CATEGORIES_CLOB WHERE CATEGORY_ID = 
?");
     // Process each preference.
     for ( ; l_arrayIndex < p_prefs.m_categories.size(); ++l_arrayIndex ){
       l_selectStmt.setString(1, p_prefs.m_categories.elementAt(l_
arrayIndex).toString());
       OracleResultSet l_selectRst = (OracleResultSet)l_
selectStmt.executeQuery();
       if (l_selectRst.next()) {
          CLOB l_clob = l_selectRst.getCLOB(1);
          l_XMLArray[l_arrayIndex] = convertFileToXML(l_clob.getAsciiStream());
       } else
          l_XMLArray[l_arrayIndex] = null ;
     }
     l_selectStmt.close();

     XMLDocHandler l_xmlHandler = new XMLDocHandler();
     l_combinedXMLDocument = l_xmlHandler.combineXMLDocunemts(l_XMLArray );
     return l_combinedXMLDocument ;
   }

4 Customizing Presentation

After fetching news items from the database, Dynamic News converts them to XML documents. XML separates content from presentation, making it easy to build custom HTML pages.

Dynamic News uses different XSL stylesheets to convert XML documents into HTML pages customized for various browsers:

It's a four-step process:

  1. Get the user's browser type.

  2. Get news items.

  3. Build an XML document.

  4. Convert XML to HTML.

Each time it receives an HTTP request, the application inspects the user-agent header to find out what kind of browser made the request. The following lines from xmlnews.dynamic.DynamicServlet.service show how the servlet creates a RequestHandler object (implemented in xmlnews/common/RequestHandler.java) and parses the request to get the browser type. Then the servlet uses this information to return an HTML page based on the end-user's preferences and browser type.

public void service(HttpServletRequest p_request, HttpServletResponse p_
response)
throws ServletException {
         ...
        // Instantiate a Request Handler (declared elsewhere)
         m_reqHandler = new RequestHandler(m_userPreference, m_general,m_
status);
         RequestParams l_reqParams = m_reqHandler.parseRequest(p_request, m_
connection);
         String l_browserType = l_reqParams.m_browserType;
         ...
         // Display the Dynamic Page
         this.sendDynamicPage(l_browserType,p_response,l_userName,m_
userPreference,
                              m_servletPath+"?REQUEST_TYPE=SET_ADVANCED_USER_
PREFS",
                m_servletPath+"?REQUEST_TYPE=LOGIN_REQUEST",
                m_servletPath+"?REQUEST_TYPE=LOG_OUT_REQUEST",
                m_servletPath);
         ...
     }

The code that actually extracts the browser type from the user-agent header resides in xmlnews.common.GenUtility.getBrowserType, which follows:

        public String getBrowserType(HttpServletRequest p_request) throws 
Exception {

          // Get all the Header Names associated with the Request
          Enumeration l_enum = p_request.getHeaderNames();

          String l_Version     = null;
          String l_browValue   = null;
          String l_browserType = null;

          while (l_enum.hasMoreElements()) {
             String l_name = (String)l_enum.nextElement();
             if (l_name.equalsIgnoreCase("user-agent"))
                 l_browValue = p_request.getHeader(l_name);
          }

          // If the value contains a String "MSIE" then it is Internet Explorer
          if (l_browValue.indexOf("MSIE") > 0 ) {
              StringTokenizer l_st = new StringTokenizer(l_browValue, ";");
              // Parse the Header to get the browser version.
              l_browserType = "IE";
              while (l_st.hasMoreTokens()) {
                 String l_tempStr = l_st.nextToken();
                 if (l_tempStr.indexOf("MSIE") > 0 ) {
                    StringTokenizer l_st1 = new StringTokenizer(l_tempStr, " ");
                    l_st1.nextToken();
                    l_Version = l_st1.nextToken();
                 }
              }
          // If the value contains a String "en" then it is Netscape
          } else if (l_browValue.indexOf("en") > 0) {
              l_browserType = "NET";
              String l_tVersion = l_browValue.substring(8);
              int l_tempInd  = l_tVersion.indexOf("[");
              l_Version = l_tVersion.substring(0, l_tempInd);
          }

          // Return the Browser Type and Version after concatenating
          return l_browserType + l_Version;
        }

After getting the end-user's browser type, the DynamicServlet's service method passes it to xmlnews.dynamic.DynamicServlet.sendDynamicPage.

This method generates HTML by fetching XML documents from the database and converting them to HTML by applying an XSL stylesheet appropriate for the end-user's browser type.

public void sendDynamicPage(String p_browserType,HttpServletResponse p_response,
  String p_userName,UserPreference p_pref,String p_userPrefURL,
  String p_signOnURL,String p_logout,
  String p_servletPath) throws Exception {
  String l_finalHTML = ""; // Holds the html
     if (p_browserType.startsWith("IE4") || (p_browserType.startsWith("IE5"))) {
         // Send the XML and XSL as parameters to get the HTML string.
         l_finalHTML = m_handler.applyXSLtoXML(
                             this.dynamicProcessingXML(m_connection, p_pref),
                             m_dyEnv.m_dynNewsHome + "/DynamicIE.xsl"
                             );
  String l_thisbit = m_general.postProcessing(l_finalHTML,p_userName,
         p_userPrefURL,p_signOnURL,p_logout,p_servletPath);
         PrintWriter l_output = p_response.getWriter();
         l_output.print(l_thisbit);
         l_output.close(); 
     } 
      else if (p_browserType.startsWith("NET4") || 
         (p_browserType.startsWith("NET5"))) {
         // Do the same thing, but apply the stylesheet "/DynamicNS.xsl"
         ...
      // When the Browser is other than IE or Netscape.
      } else {
      // Do the same thing, but apply the stylesheet "/Dynamic.xsl"
      ...
      }
   }

The key methods are:

Importing and Exporting News Items

Dynamic News can also import and export XML documents that conform to the Resource description framework Site Summary (RSS) standard. Developed by Netscape as a way to share data channels, RSS is used at Web sites such as my.netscape.com and slashdot.org.

An application can use RSS to syndicate its news pages (making them available to RSS hosts) and to aggregate news from other RSS sites. For example, Dynamic News includes the xmlnews.admin.RSSHandler class. It uses a specified DTD to parse and extract news items from a specified file, and then it stores the items in a hashtable. The class also provides a method that returns the elements in that hashtable.


Go to previous page Go to next page
Oracle
Copyright © 1996-2001, Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index