Application Techniques



Using an Invoked Object to Cache Data

How to use caching to eliminate retrieving data each time a page request is made.

About this technique

Details

Category

Triggered Business Object Techniques> General

Description

You'll learn about:

You can run this technique code from:

NOTE   First make sure that database is running on your localhost SilverStream Server

Related reading

See the chapter on Business Objects Basics in the Programmer's Guide

This technique shows how to cache data on the server so that the data is fetched once, but not re-fetched unless the data changes. It uses a single business object, boCacheDepartmentList, that implements AgiServerListener, AgiInvokedListener, and AgiTableListener. This object declares 3 variables in the General section of the code:

  // boolean that is reset in afterTableChanged evt       
  boolean m_bDataChanged = false; 
  // Stores cached data  
  Vector m_vctDepartments = null; 
  // Stores timestamp when data is retrieved 
  long m_timestamp = 0; 

Using the serverStarted event to cache data   Top of page

The code for the serverStarted event instantiates an AgaData and caches it on the server. It does this by passing a user-defined method, retrieveTheData(), to getAgaData().

The user-defined method stores the result in the Class variable m_vctDepartments, which makes the data available to the invoked event.

  public void serverStarted(AgoServerStartEvent evt) 
  { 
        // Retrieve the Departments 
        try 
        { 
           AgaData dataDepartments = 
               evt.getAgaData("dataDepartments"); 
           retrieveTheData( dataDepartments ); 
        } 
        catch (Exception e) 
        { 
        } 
        return; 
  } 

Invoking the object and managing the result    Top of page

In the PageRequestBegin event on pgDepartmentListCached, the code gets the last modified time of the data during the client session, and calls the business object that manages the department list cache, passing the cached page last modified time.

This code fragment shows how the page invokes the business object, passing it the last modified time of the cached page.

  protected void pageRequestBegin(AgiHttpServletRequest req, AgiHttpServletResponse res) throws Exception 
  { 
    AgoHttpRequestEvent hRequest = (AgoHttpRequestEvent) req; 
    AgiHttpServletResponse hResponse = (AgiHttpServletResponse) res; 
     
        // Get time cached page was last modified 
        long lTime = hRequest.getDateHeader("If-Modified- 
               Since")/1000*1000; 
        try 
        // invoke the business object 
        { 
           m_vctDepartments = (Vector) invokeBusinessObject( 
              "com.examples.utilityobjects.boCacheDepartmentList",  
               new Long(lTime)); 
        } 
        catch (NullPointerException npe) 
        { 
           throw new Exception(MSG_OBJECT_NOT_FOUND); 
        } 
        catch (Exception e) 
        { 
           throw new Exception(e.toString()); 
        } 

If the data cached by the server has changed since this page was last sent to the browser, the invoked object returns a new vector. If it hasn't changed, it returns null. If it returns null, the page prevents a re-fetch of the data by throwing an exception, which is handled under handlePagException:

        ... 
        if (m_vctDepartments == null) 
        { 
           // Prevent building of the page and set the 
           // response header to not-modified. 
            // To do this, throw an Exception and handle it in the  
           // handlePageException method. 
           throw new Exception("NOT_MODIFIED"); 
        } 

Coding the handlePageException   Top of page

This is how the exception is handled:

  public boolean handlePageException(Exception exception, AgiHttpServletRequest req, AgiHttpServletResponse res) 
  { 
     // See if this is an Exception thrown in pageRequestBegin  
     // so to avoid rebuilding the page. 
     if (exception.getMessage().equals("NOT_MODIFIED")) 
        {                
           AgiHttpServletResponse hResponse = 
               (AgiHttpServletResponse) res; 
           hResponse.setStatus(hResponse.SC_NOT_MODIFIED); 
           return (true);    
        } 
        // account for other Exception 
        else 
        { 
           return(false); 
        } 
  } 

Resetting the last-modified date   Top of page

Finally, the response header resets the Last-Modified date on the response. This is done under the code section for pageRequestEnd:

  protected void pageRequestEnd(AgiHttpServletRequest req, AgiHttpServletResponse res) throws Exception 
   { 
    AgiHttpServletResponse hResponse = (AgiHttpServletResponse) res; 
      
        // Update the 'Last-Modified' date in the response  
        // header before leaving. 
        long lCurrentTime = System.currentTimeMillis()/1000*1000; 
                     
        // Update the response header fields 
        hResponse.setHeader("Cache-Control", null);  
        hResponse.setHeader("Pragma", null); 
        hResponse.setDateHeader("Last-Modified", lCurrentTime); 
        hResponse.setStatus(hResponse.SC_OK);    
         
  } 

Using the invoked object to determine if data is current    Top of page

The code for the invoked event checks to see if the cached data was modified. If the data is unchanged it returns null. If the data has changed it invokes retrieveThedata() and sets the result on m_vctDepartments.

  public void invoked(AgoInvokedEvent evt) throws Exception 
  { 
        Long longTimeStamp = (Long) evt.getParameter(); 
        long lTimeStamp = 0; 
        if (longTimeStamp != null) 
           lTimeStamp = longTimeStamp.longValue(); 
        //  
        // If data was not inially retrieved, or it has changed... 
        if (m_vctDepartments == null || m_bDataChanged) 
        { 
           AgaData dataDepartments =  
              evt.getAgaData("dataDepartments"); 
           retrieveTheData( dataDepartments ); 
           evt.setResult(m_vctDepartments); 
           return; 
        } 
        else 
        { 
           if (lTimeStamp <= 0 || lTimeStamp < m_timestamp) 
           { 
              evt.setResult(m_vctDepartments); 
              return; 
           } 
           else 
           { 
              // return null if the data has not changed 
              evt.setResult(null); 
              return; 
           } 
        } 
  } 

Setting the data modified flag   Top of page

When invoked, the afterTableChanged event simply resets the boolean class variable. This value is checked in the invoked event.

  public void afterTableChange(AgoTableEvent evt) 
  {  
        m_bDataChanged = true; 
      
        return; 
  } 

Getting and storing the data in the Vector variable   Top of page

This is the method that is initially called by the serverStart event Note the importance of storing the result in a class variable (m_vctDepartments), where it is accessible to the invoked event on the client.

  public void retrieveTheData(AgaData phAgData) throws java.io.IOException 
     { 
        // Query the database 
        try 
        { 
           phAgData.query(null); 
      
           m_vctDepartments = new Vector(); 
           boolean bResult = phAgData.gotoFirst(); 
               
           int iCount =0; 
           while (bResult) 
           // get the data 
           { 
              String[] sRowValues = { (String) ( (Integer) 
                phAgData.getProperty("departmentid")).toString(), 
                  (String) phAgData.getProperty("departmentname") }; 
                 // populate the Class vector variable with the  
                 // data when the server is started.  
                 // This provides access to the data  
                 // from the invoked event  
              m_vctDepartments.addElement(sRowValues); 
              bResult = phAgData.gotoNext(); 
           } 
           // reinitialize the Class variable to false 
           m_bDataChanged = false; 
           // reset the Class variable timestamp value 
           m_timestamp = System.currentTimeMillis(); 
        } 
         
        catch (Exception e) 
        { 
           // reset Class variable to null 
           m_vctDepartments = null; 
           throw new java.io.IOException(e.toString()); 
        } 
     } 
   





Copyright © 2000, SilverStream Software, Inc. All rights reserved.