Home for HMNL Enterprise Computing

Dynamic Display Applet

Ian Tree  10 May 2006 14:00:35

Dynamic Display Applet


The LastErrorMessage Applet below is intended for implementation in a Notes (client) application. The Notes application is "probe" application that contains a background agent that performs queries on a regular basis, if any of these queries fails then the agent creates an "Error" document containing information to indicate what the failure is. The Applet is intended to be a display element that is incorporated in a Page and this page is included in one Frame of a Frameset from which the application is used. The Applet will display the contents of the last "Error" reported by the background agent.

The code presented below is complete working code but includes diagnostic print output that would not be included in a live implementation and is optimised for readability.

You will need an understanding of Java, Multi-Threading, AWT Applet, Domino Objects.
  • Design Pattern
  • Design Notes
  • Code - LastErrorMessage Applet
  • Implementation Notes

Design Pattern


The Applet is implemented following a two thread model the controller and the daemon thread. The controller thread is responsible for servicing all standard Applet requests, adhering to the contract for a well-behaved Applet, it is also responsible for creating, initiating and terminating the daemon thread. The daemon thread is responsible for monitoring the view of "Error" documents and responding to the detection of a new entry by displaying key information from the "Error" document in the Applet's display area. The controller thread makes use of the NotesAppletContext object, using this object greatly reduces the setup overheads involved in locating resources in an Applet that is being used in a Notes Client.


Design Notes


The Applet exploits the "thread-safe" behaviour of the Domino Objects, the Session, Database and View are created in the main thread but are used in the monitor thread. This is perfectly safe and acceptable but the objects must be recycled by the thread in which they were created.
The controller thread makes use of the NotesAppletContext object, using this object greatly reduces the setup overheads involved in locating resources in an Applet that is being used in a Notes Client.
The static sinitThread and stermThread must used in the daemon monitor thread as this is the only construct possible in this configuration.
The bRunMonitor semaphore is used bi-directionally both to signal to the daemon monitor thread that it must close down and then re-set by the daemon monitor thread to signal to the control thread that it can continue with the "stop" processing.
The "stop" process uses Thread.interrupt() to dispatch the monitor thread from it's sleeping state.

Code - LastErrorMessage Applet


import java.awt.*;
import lotus.domino.*;

public class LastErrorMessage extends AppletBase implements Runnable
{

   /********************************************************************************************/
   /*  LastErrorMessage                                                                        */
   /*                                                                                          */
   /*  Version:  1.1                                                                           */
   /*                                                                                          */
   /*  This Applet class is a simple AWT Applet implementation designed to run in a Notes      */
   /*  client environment. The Applet shows how asynchronous updates can be made to the Notes  */
   /*  UI through the use of Applets. A typical implementation would be to insert the Applet   */
   /*  into a Page and have the Page displayed as part of a Frameset. This implementation is   */
   /*  based on a probe application and maintains a current display of the latest Error        */
   /*  document that appears in the application.                                               */
   /*                                                                                          */
   /*  Author: Ian Tree - Hadleigh Marshall Netherlands - 2004                                 */
   /********************************************************************************************/



   Thread tMonitor = null;
   Session sessCurrent = null;
   NotesAppletContext nacCurrent = null;
   Database dbCurrent = null;
   View vErrors = null;
   Document docError = null;
   Item itmTime = null;
   Name nnServer = null;
   DateTime dtMessage = null;
   String strMessage = null;

   GridBagLayout gblCurrent = new GridBagLayout();
   GridBagConstraints gbcCurrent = new GridBagConstraints();

   Label lServer = new Label("Server Name", Label.LEFT);
   Label lTime = new Label("Time", Label.LEFT);
   Label lMsg = new Label("Message", Label.LEFT);

   Font fArialBold = null;
   Font fCurrent = null;
   Color colCurrent = Color.red;

   long lRefreshCycleDelay = 15 * 1000;    //  15 Seconds

   boolean bRunMonitor = true;

   public void notesAppletInit()
   {

       /*************************************************************************************/
       /*  notesAppletInit                                                                  */
       /*                                                                                   */
       /*  This method is overriden in AppletBase and is called when the Applet is loaded.  */
       /*  The method should instantiate application resources that are used over the       */
       /*  duration of the Applet lifecycle.                                                */
       /*                                                                                   */
       /*************************************************************************************/


       fArialBold = Font.decode("Arial-BOLD-12");
       fCurrent = fArialBold;
       setLayout(gblCurrent);
       gbcCurrent.fill = GridBagConstraints.BOTH;
       gbcCurrent.weightx = 0.3;
       addComponent(lServer);
       addComponent(lTime);
       gbcCurrent.weightx = 1.0;
       addComponent(lMsg);
       setVisible(true);
   }

   public void notesAppletStart()
   {

       /********************************************************************************/
       /*  notesAppletStart                                  
                      */
       /*                                                                              */
       /*  This method is overriden in AppletBase and is called when the Applet has    */
       /*  completed initialisation. The method should instantiate dynamic resources   */
       /*  and construct the monitoring thread. Note that the start and stop may be    */
       /*  invoked multiple times during the Applet lifecycle.                         */
       /*                                                                              */
       /********************************************************************************/


       try
       {
           sessCurrent = this.openSession();
           if (sessCurrent == null)
           {
               System.out.println("ERROR: Failed to open session.");
           }
           else
           {
               System.out.println("INFO: Notes Session is open.");
               nacCurrent = getContext(sessCurrent);
               if (nacCurrent == null)
               {
                   System.out.println("ERROR: Unable to obtain the Applet context.");
               }
               else
               {
                   System.out.println("INFO: Applet context obtained, server is: " + nacCurrent.getServer() + ".");
                   dbCurrent = nacCurrent.getDatabase();
                   if (dbCurrent == null)
                   {
                       System.out.println("ERROR: Current database could not be located.");
                   }
                   else
                   {
                       System.out.println("INFO: Current database located.");
                       vErrors = dbCurrent.getView("Errors");
                       if (vErrors == null)
                       {
                           System.out.println("ERROR: Unable to open the Errors view.");
                       }
                       else
                       {
                           System.out.println("INFO: Errors view was located.");
                           if (tMonitor == null)
                           {
                               tMonitor = new Thread(this);
                               tMonitor.start();
                               System.out.println("INFO: The monitor thread has been started.");
                           }
                       }
                   }
               }
           }
           return;
       }
       catch (Exception eApp)
       {
           eApp.printStackTrace();
       }
   }

   public void notesAppletStop()
   {

       /**************************************************************************************/
       /*  notesAppletStop                                                                   */
       /*                                                                                    */
       /*  This method is overriden in AppletBase and is called when the Applet is about     */
       /*  to be destroyed or when stop is requested. The method should signal the monitor   */
       /*  thread to stop and release any resources that were acquired in the start method.  */
       /*                                                                                    */
       /**************************************************************************************/


       System.out.println("INFO: Applet Stop has been triggered.");
       bRunMonitor = false;
       try
       {
           bRunMonitor = false;
           tMonitor.interrupt();
           while (!bRunMonitor)
           {
               Thread.sl
eep(50);
           }
           tMonitor = null;
           System.out.println("INFO: Monitor thread has been closed down.");
           if (vErrors != null) vErrors.recycle();
           if (dbCurrent != null) dbCurrent.recycle();
           if (sessCurrent != null) this.closeSession(sessCurrent);
           System.out.println("INFO: All Applet resources have been freed.");
       }
       catch (Exception eAS)
       {
           eAS.printStackTrace();
       }
   }

   void addComponent(Component cCurrent)
   {

       /******************************************************************************/
       /*  addComponent                                                              */
       /*                                                                            */
       /*  This method adds AWT components to the current graphics environment.      */
       /*  The method sets common attributes to the components as they are added.    */
       /*                                                                            */
       /******************************************************************************/


       cCurrent.setFont(fCurrent);
       cCurrent.setForeground(colCurrent);
       gblCurrent.setConstraints(cCurrent, gbcCurrent);
       add(cCurrent);
   }

   public void run()
   {
       /**************************************************************************************/
       /*  run                                                                               */
       /*                                                                                    */
       /*  This method is executed as a separate thread from the other code. This is the     */
       /*  monitor thread. The thread checks for the latest Error document to appear in      */
       /*  view and displays it in the graphics environment. The thread also checks an       */
       /*  internal control variable to detect when it is asked to be stopped.               */
       /*                                                                                    */
       /**************************************************************************************/


       try
       {
           NotesThread.sinitThread();
           System.out.println("INFO: Monitor thread has initialised.");
           while (bRunMonitor)
           {
               vErrors.refresh();
               docError = vErrors.getFirstDocument();
               if (docError == null)
               {
                   System.out.println("ERROR: Unable to retrieve latest error document.");
               }
               else
               {
                   System.out.println("INFO: Latest Error document has been located.");
                   String strServer = docError.getItemValueString("ServerName");
                   nnServer = sessCurrent.createName(strServer);
                   lServer.setText(nnServer.getCommon());
                   String strMsg = docError.getItemValueString("ErrorDetail");
                   lMsg.setText(strMsg);
                   itmTime = docError.getFirstItem("RunTime");
                   if (itmTime != null)
                   {
                       lTime.setText(itmTime.getDateTimeValue().getLocalTime());
                       itmTime.recycle();
                   }
                   docError.recycle();
                   docError = null;
               }
               try
               {
                   Thread.sleep(lRefreshCycleDelay);
               }
               catch (Exception eSleep)
               {
                   System.out.println("
INFO: Monitor thread has been interrupted.");
               }
           }
           if (docError != null)
           {
               docError.recycle();
               docError null;
           }
           bRunMonitor = true;
       }
       catch (Exception eMon)
       {
           System.out.println("ERROR: Exception has been thrown in the monitor thread.");
           eMon.printStackTrace();
       }
       finally
       {
           NotesThread.stermThread();
       }
       System.out.println("INFO: Monitor thread has completed local closeure.");
   }
}

Implementation Notes

An experimental setup can easily be developed to exercise the applet functionality.

Create a scheduled agent to write "Error" documents at regular intervals (visibly change the data content so that changes are easily detected). Create a view called "Errors" that dsiplays the data in decending sequence of "RunTime". Then complile the Applet source and import it as a resource into the database. Finally create a means of displaying the Applet such as a Page and a Frameset containing the page that is opened when the database is opened.
Comments