Package com.bitmovers.maui.engine.httpserver

Source Code of com.bitmovers.maui.engine.httpserver.HTTPEventTranslator

// =============================================================================
// com.bitmovers.maui.HTTPEventTranslator
// =============================================================================

package com.bitmovers.maui.engine.httpserver;

import java.io.*;
import java.net.*;
import java.util.*;
import com.bitmovers.maui.profiler.Profiler;
import com.bitmovers.maui.*;
import com.bitmovers.maui.engine.*;
import com.bitmovers.maui.engine.httpserver.*;
import com.bitmovers.maui.engine.logmanager.*;
import com.bitmovers.maui.engine.resourcemanager.*;
import com.bitmovers.maui.events.*;
import com.bitmovers.maui.layouts.*;
import com.bitmovers.maui.components.*;
import com.bitmovers.maui.components.foundation.*;
import com.bitmovers.maui.engine.render.I_HasMultiple;
import com.bitmovers.maui.engine.render.I_Renderer;
//import com.bitmovers.maui.components.complex.*;
import com.bitmovers.utilities.*;


// =============================================================================
// <<SINGLETON>> CLASS: HTTPEventTranslator
// =============================================================================
/**
* HTTPEventTranslator SINGLETON <p>
* @invisible
* This is the main event translator for HTTP requests.  It is responsible for determining what to do with
* with the parsed HTTP Request.  It is a bit of a workhorse object, functioning much as a switch between the
* HTTP request and the MauiApplication environments.  it arbitrates the creation and initialization of MauiApplications,
* switching between MauiApplications, event dispatching to components within MauiApplications, and resource retrieval.
*/
public class HTTPEventTranslator
{
  // ---------------------------------------------------------------------------
 
  private static HTTPEventTranslator theInstance = new HTTPEventTranslator();
 
  private static final String [] REQUESTHEADERS  = new String [] {"ua-cpu",
                                    "ua-os",
                                    "user-agent",
                                    "accept",
                                    "x-up-devcap-screenpixels"}// For UP browsers

  private Hashtable mPIDLookupTable;
 
  private long startTime;
 
  private boolean initDone = false;
  private ApplicationManager am;
  private ComponentManager cm;
  private MDesktop desktop;
 
 
  // ---------------------------------------------------------------------------
  // CONSTRUCTOR: HTTPEventTranslator
  // ---------------------------------------------------------------------------
 
  /**
  * Since the HTTPEventTranslator is a singleton, the constructor is private, thus preventing
  * any explicit construction
  */
  private HTTPEventTranslator()
  {
      this.mPIDLookupTable = new Hashtable();   
   
  }
 

  // ---------------------------------------------------------------------------
  // METHOD: initialize
  // ---------------------------------------------------------------------------
 
  /**
  * Initialize the HTTPEventTranslator.  This involves getting references to a few required singleton Maui objects.
  * This is done only once, at startup time.
  */
  public void initialize()
  {
    if (!initDone)
    {
      ServerConfigurationManager theSCM = ServerConfigurationManager.getInstance();
      am = ApplicationManager.getInstance();
      cm = ComponentManager.getInstance ();
      desktop = MDesktop.getInstance ();
     
      initDone = true;
      System.err.println(new DebugString("[HTTPEventTranslator] - Started."));
    }
  }


  // ---------------------------------------------------------------------------
  // CLASS METHOD: getInstance
  // ---------------------------------------------------------------------------
 
  /**
  * Return an instance of the HTTPEventTranslator.
  *
  * @return The HTTPEventTranslator object
  */
  public static HTTPEventTranslator getInstance()
  {
    return theInstance;
  }
   
 
  /**
  * Build a description of the request header.  This is used during the creation of a MauiApplication.  Some information
  * from the HTTPRequest is required by the ApplicationManager to determine characteristics of the MauiApplication being
  * created.
  *
  * @param aRequest The HTTPRequest object
  *
  * @return A Map containing the required header values from the HTTPRequest
  */
  private Map buildRequestHeaderDescription (HTTPRequest aRequest)
  {
    Map retVal = new HashMap ();
   
    String theValue = null;
    for (int i = 0; i < REQUESTHEADERS.length; i++)
    {
      theValue = aRequest.getHeaderValue (REQUESTHEADERS [i]);
      if (theValue != null)
      {
        retVal.put (REQUESTHEADERS [i], theValue);
      }
    }
    return retVal;
  }
   
  // ---------------------------------------------------------------------------
  // METHOD: translateRequest
  // ---------------------------------------------------------------------------
 
  /**
  * This is primary interface to HTTPRequest.  It determines what to do with the HTTPRequest
  *
  * @param request The HTTPRequest
  *
  * @return The HTTPResponse object, which contains the full response buffer
  */
  public HTTPResponse translateRequest (HTTPRequest request)
    throws SessionMaximumException
  {
    HTTPResponse response = request.createResponseObject ();
    Compositor theCompositor = null;
    MauiApplication application = null;
      boolean theOutOfDate = false;
    String theApplicationName = null;
    int theReference = 0;
    boolean theCreate = false;
 
    try
    {
      // return a GIF
      if (request.getQueryValue("getImage") != null)
      {
        try
        {
          ResourceDescription description = new ResourceDescription(request.getQueries());
         
          byte[] gifImage = ResourceManager.getInstance().getResourceBytes(description);
          response.setContentType("image/gif");
          response.setContent(gifImage);
          gifImage = null;
        }
        catch (ResourceNotFoundException exception)
        {
          System.out.println(new ErrorString(exception, "HTTPEventTranslator.translateRequest()"));
        }
      }
      // return a Style Sheet
      else if (request.getQueryValue("getCSS") != null)
      {
        try
        {
          //String css = new String(MauiApplication.getJarResources ().getResource(request.getQueryValue("getCSS")));
          byte[] css = ResourceManager.getInstance().getResourceBytes(request.getQueryValue("getCSS"));
          response.setContentType("text/css");
          response.setContent(css);
          css = null;
        }
        catch (ResourceNotFoundException exception)
        {
          System.out.println(new ErrorString(exception, "HTTPEventTranslator.translateRequest()"));
        }
      }
      // return a HTML/WML/etc. page
      else
      {
        HTTPSession theSession = null;
        synchronized (this)
        {
              // Lookup the mPID (Maui Application PID) based on the request's
              // session ID.  If the session is new, then construct a new Maui
              // application and enter it into the pool.  Do the same if the
              // mPID lookup up is no longer valid.
              theSession = request.getSession ();
        }
       
        Object theSynchBlock = theSession.getSynchBlock ();
        synchronized (theSynchBlock)
        {
              String sessionID = theSession.getID();
              String mPID = (String)this.mPIDLookupTable.get(sessionID);
             
              theApplicationName = request.getApplicationName ();
              if (theApplicationName.endsWith ("/"))
              {
                theApplicationName = theApplicationName.substring (0, theApplicationName.length () - 1);
              }
             
              theReference = Profiler.start (MauiRuntimeEngine.SOURCE_APPLICATION,
                               MauiRuntimeEngine.ACTION_CREATE);
              theCreate = true;
                boolean theDoLoad = false;
             
              if ((mPID == null) || !(ProcessManager.getInstance().processExists(mPID)))
              {
                //
                //  This is a new session, so an application has to be loaded for it.
                //  The file portion of the URL indicates what application is expected.
                //  If there is no file portion, then use the default application.
                //
                application = am.createMauiApplication (buildRequestHeaderDescription (request),
                                    request.getCookies (),
                                    theSession,
                                    theApplicationName,
                                    true);
                                   
                if (application.isDefaultApplication () &&
                  !am.isDefaultApplicationClassName (theApplicationName))
                {
                  theApplicationName = am.getDefaultApplicationClassName ();
                }
              this.mPIDLookupTable.put(sessionID, application.getProcessID());
              }
              else
              {
                //
                //  The process id was found, which means that the application exists.
                //
                application = ProcessManager.getInstance().getProcess (mPID);
                String theApplicationAddress = null;
               
                //
                //  Check if the URL indicates that a different application should be run (ie. application chaining)
                //
               
                if (theApplicationName == null ||
                    theApplicationName.trim().length () == 0)
                {
                  if (!application.isDefaultApplication ())
                  {
                    //
                    //  The default application has been requested but the current application isn't the default.
                    //  So load the default application
                    //
                    theDoLoad = true;
                  }
                }
                else if (!application.getApplicationAddress ().equals (
                          (theApplicationAddress =am.getMauiApplicationAddress (theApplicationName))))
                {
                  //
                  //  The URL indicates that a different application has been requested (probably due to application
                  //  chaining, or a new application name explicitly being entered by the user).  So load the new application.
                  //
                  theDoLoad = true;
                }
               
                if (theDoLoad)
                {
                  //
                  //  The application in the URL is different from the current one.  So, chain to the new one
                  //
                  if ((application = theSession.getCachedMauiApplication (theApplicationName)) == null)
                  {
                  application = am.createMauiApplication (buildRequestHeaderDescription (request),
                                      request.getCookies (),
                                      theSession,
                                      theApplicationName,
                                      true);
                  if (application.isDefaultApplication () &&
                    !am.isDefaultApplicationClassName (theApplicationName))
                  {
                    theApplicationName = am.getDefaultApplicationClassName ();
                  }
                }
              mPIDLookupTable.put(sessionID, application.getProcessID());
                }
                else
                {
                  theCreate = false;
                  theOutOfDate = am.isOutOfDate (application);
                }
              }
        }

        theSynchBlock = application.getSynchBlock ();
        synchronized (theSynchBlock)
        {
             
          application.setRuntimeName ((theApplicationName == null ? "" : theApplicationName));
          application.setInboundCookies (request.getCookies ());
          application.setPath (request.getApplicationName ());
              theSession.setApplication (application);
              desktop.setApplication (application);
              cm.setApplication (application);

          Profiler.finish (theReference,
                   MauiRuntimeEngine.SOURCE_APPLICATION,
                   (theCreate ? MauiRuntimeEngine.ACTION_CREATE :
                           MauiRuntimeEngine.ACTION_CACHE_HIT),
                    application.getRuntimeName ());
         
          boolean theDoChain = false;
              String theChainApplicationName = null;
          if (!theOutOfDate)
          {
            HTTPSession.activateApplication (application);
                this.translateAndDispatchEvents(request);
            HTTPSession.deactivateApplication (application);
             
                theChainApplicationName = application.getChainApplicationName ();
                if (theChainApplicationName != null &&
                  ! (application.getRuntimeName ().equals (theChainApplicationName)))
                {
                  //
                  //  There is a request to switch to another MauiApplication.
                  //  So, initiate application chaining.  HTTPResponse will take care of the
                  //  HTTP redirect
                  //
                  theDoChain = true;
                  String theFolderName = am.getFolderName (theChainApplicationName);
                  if (theFolderName.length () > 0)
                  {
                    theFolderName = theFolderName + "/";
                  }
                  theChainApplicationName = theFolderName + am.getSimpleShortName (theChainApplicationName);
                  String theServletURL = application.getServletURL ();
                  if (theServletURL.length () > 1)
                  {
                    theChainApplicationName = theServletURL + theChainApplicationName;
                  }
                  application.setChainApplicationName (null);
                 
                  //
                  //  Invalidate the application's window so that it will be completed re-rendered whenever
                  //  the client returns to it.  It's possible that the application state will be stale.
                  //
                  application.invalidate ();
                  //
                  //  Chaining to another application
                  //
                  /*application = am.createMauiApplication (buildRequestHeaderDescription (request),
                                      sessionID,
                                      theApplicationName,
                                      true);
              application.setRuntimeName ((theApplicationName == null ? "" : theApplicationName));
                  theSession.setApplication (application);
                  desktop.setApplication (application);*/
                }
            }
           
            if (application.isExit () ||
                application.isExitAll ())
              {
                //
                //  The application, or applications are exiting.  So remove the application references from the session,
                //  and set the redirection to the default application.
                //
                String theRedirection;
                if (!theOutOfDate)
                {
                  theSession.removeApplication ((application.isExitAll () ? null : application));
                  if ((theRedirection = application.getURLString ()) == null)
                  {
                    theRedirection = (theDoChain ? theChainApplicationName : "");
                  }
                }
                else
                {
                  theRedirection = application.getChainApplicationName ();
                }
                response.setRedirection (theRedirection);
              }
              else if (theDoChain)
              {
                //
                //  Application chaining is occuring so there is no need to do any rendering for the current request
                //
                response.setRedirection (theChainApplicationName);
                response.setPseudoEvents (application.getPseudoEvents ());
                application.resetPseudoEvents ();
              }
              else
              {
                // Now obtain a compositor for the device in questions.  To do this,
                // we must first assemble a Vector of relevant platform information
                // in order for the Composition Manager to give us what we need.

                theCompositor = application.getCompositor ()//  Compositor can be determined at application creation time

                // Now we must do the composition.  The HTMLCompositor requires the
                // MauiApplication in use, as well as HTTPRequest and HTTPResponse
                // objects.  Obviously this isn't a good coupling for compositors in
                // general (although the WMLCompositor accepts the same thing) ...
                // this binding/interface will be re-visited, but this is the
                // mechanism in use for now.
               
                Hashtable parameters = new Hashtable();
                parameters.put("application", application);
                parameters.put("request", request);
                parameters.put("response", response);
               
                theCompositor.doComposition(parameters);
               
                //
                //  If redirection was set, pass the new URL onto the HTTPResponse object
                //
                response.setRedirection (application.getURLString ());
                application.setURLString (null);
          }
        }
      }
    }
    catch (Exception exception)
    {
      if (exception instanceof SessionMaximumException)
      {
        throw (SessionMaximumException) exception;
      }
     
      if (application != null)
      {
        HTTPSession.deactivateApplication (application);
      }
      //
      //  Something went wrong during the request processing.  Try to send some kind of informative error message to the
      //  client.
      //
      if (theCompositor == null)
      {
        if (application != null)
        {
          theCompositor = application.getCompositor ();
        }
      }
     
      if (theCompositor != null)
      {
        response.setContentType (theCompositor.getBaseContentType ());
        response.setContent ((theCompositor.generateExceptionMessage (exception)).getBytes ());
      }
      exception.printStackTrace(System.err);
    }
   
    return response;
  }


  // ---------------------------------------------------------------------------
  // METHOD: translateAndDispatchEvents
  // ----------------------------------------------------------------------
 
  /** This method is responsible for extracting and parsing the components id's in the MauiEventQueue,
    * getting their respective posted values, creating MActionEvent objects, and publishing them
    *
    * @param request The HTTPRequest (All of the posted information is available through this object)
    *
    */
   
  private void translateAndDispatchEvents(HTTPRequest request)
  {

    Vector queue = new Vector();
    Vector theMultipleEvents = new Vector ();
    boolean theAllowDuplicateEvents = true;

    //
    //  Pseudo events are events which are constructed from information contained within the base URL (ie. not parameterized, and
    //  prior to the "?" in the URL string.  This is to compensate for caching strategies in some WML browsers (eg. Nokia)
    //  which cause bad URL's.
    //
    String [] thePseudoEvents = request.getPseudoEvents ();
    for (int i = 0; i < thePseudoEvents.length; i++)
    {
      processOneEvent (request, thePseudoEvents [i], theMultipleEvents);
    }
   
    String queueString = request.getQueryValue("mauiEventQueue");
   
    //
    // Look for lonely non-MSettable componentIDs in the query string and tack them on to the end of
    // the event queue. This is necessary for clients which do not support client-side scripting (like
    // Lynx and Palm Web Clippings). [ian - 2001.06.25]
    //   
    Enumeration queryKeys = request.getQueries().keys();
    while (queryKeys.hasMoreElements())
    {
      String key = (String)queryKeys.nextElement();
      if (cm.getComponent(key) != null && !(cm.getComponent(key) instanceof MSettable))
      {
        if (queueString != null && !queueString.equals(""))
        {
          queueString += "," + key;
        }
        else
        {
          queueString = key;
        }
      }
    }

    if (queueString != null)
    {
      //
      //  There is data for the mauiEventQueue, so it probably means that there are some events
      //  to process.
      //
      StringTokenizer queueTokens = new StringTokenizer(queueString, ",");
      int theSequenceNumber = -1;
      boolean theIsDuplicateEvent = false;
      while (queueTokens.hasMoreTokens() && theAllowDuplicateEvents)
      {
        //
        //  Construct a Vector of the events.  This step is taken to prevent duplication of
        //  componentID's within the queue
        //
        String componentID = queueTokens.nextToken();
        // Ensure that each component appears only once in the queue.
        if (!queue.contains(componentID))
        {
          MComponent theComponent = getComponent (componentID);
          if (theComponent != null)
          {
            if (theSequenceNumber == -1)
            {
              MComponent theRootParent = theComponent.getRootParent ();
              if (theRootParent instanceof MauiApplication)
              {
                MauiApplication theApplication = (MauiApplication) theRootParent;
                String theSequenceString = request.getQueryValue ("sequenceNumber");
                if (theSequenceString != null)
                {
                  theSequenceNumber = Integer.parseInt (theSequenceString);
                  theIsDuplicateEvent = theApplication.isDuplicateEvent (theSequenceNumber);
                  if (!theApplication.getAllowDuplicateEvents ())
                  {
                    theAllowDuplicateEvents = !theIsDuplicateEvent;
                  }
                }
                else
                {
                  theSequenceNumber = -2;
                }

                       
              }
            }
                 
            if (theSequenceNumber != -2 &&
                theAllowDuplicateEvents &&
                !theComponent.getAllowDuplicateEvents ())
            {
              theAllowDuplicateEvents = !theIsDuplicateEvent;
            }
                   
          }
         
          //++ 405 JL 2001.09.21
          // Warning!!! HACK Alert. This is a hack! The HTTPEventTranslator should know nothing about
          // MRadioButtons but lo and behold, component specific code.
          if (theComponent instanceof MRadioButton)
          {
              //  if the component is an MRadioButton, add if it appears in the Query string
              //  (ie. it was the selected member of its group) add it to the event queue
                       
                        if (request.getQueries().values().contains(componentID))
              {
                            queue.addElement(componentID);
                        }
          }
          else
          {
                  queue.addElement(componentID);
          }
          //--
         
        }
      }

      if (theAllowDuplicateEvents)
      {
 
        Enumeration queueElements = queue.elements();

        //
        //  Process an event for each component id in the event Vector
        //
        while (queueElements.hasMoreElements())
        {
          String componentID = (String)queueElements.nextElement();         
          processOneEvent (request, componentID, theMultipleEvents);
        }
      }
    }
      
    if (theAllowDuplicateEvents)
    {
      //
      //  Because of scripting limitations, WML also has a couple of special variables which describe a single
      //  event rather than a complete array of events.
      //
      String oneEvent = request.getQueryValue ("mauiOneEvent");
      if (oneEvent != null)
      {
        if (!queue.contains (oneEvent))
        {
          processOneEvent (request, oneEvent, theMultipleEvents);
        }
      }
    }
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getComponent
  // ----------------------------------------------------------------------
 
  /** Get a component
    *
    * @param aComponentID The componentID to use for lookup
    *
    * @return The corresponding component
    *
    */
   
  private MComponent getComponent (String aComponentID)
  {
    int theUnderScore = aComponentID.indexOf ("z");
    String theLookupComponentID = (theUnderScore != -1 ?
                      aComponentID.substring (theUnderScore + 1) :
                      aComponentID);

    // NOTE ** This code must be audited for security breeches and stuff like
    //         that.  Spoofing of other people's events may become an issue...

    return ComponentManager.getInstance().getComponent (theLookupComponentID);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: processOneEvent
  // ----------------------------------------------------------------------
 
  /**
  * Process a single event.
  *
  * @param request The HTTPRequest
  * @param componentID The target component id for the event.  This is used to retrieve the event value from the
  * @param aMultipleEvents Vector containing a list of components accumulated for a multiple event component.
  *
  */      
    private void processOneEvent (HTTPRequest request, String componentID, Vector aMultipleEvents)
    {
      MComponent component = getComponent (componentID);
     
        if (component != null && component.isEnabled())
        {
            String stateData = request.getQueryValue(componentID);
            if (stateData != null || request.isEmptySignificant ())
            {
            I_Renderer theRenderer = component.getRenderer ();
            if (theRenderer instanceof I_HasMultiple)
            {
              String [] theComponentIDs = ((I_HasMultiple) theRenderer).getComponentIDs (componentID, stateData);
              for (int i = 0; i < theComponentIDs.length; i++)
              {
                if (!aMultipleEvents.contains (theComponentIDs [i]))
                {
                  aMultipleEvents.addElement (theComponentIDs [i]);
                  component = getComponent (theComponentIDs [i]);
                  processOneActionEvent (component,
                               ((I_HasMultiple) theRenderer).
                                       getStateData (theComponentIDs [i],
                                                request.getQueryValue (theComponentIDs [i])));
                }
              }
            }
            else
            {
              processOneActionEvent (component, stateData);
            }
      }
        }
    }
   
    /**
    * Process a single action event
    *
    * @param aComponent The component to process
    * @param aStateData The state data for the component
    */
    private void processOneActionEvent (MComponent aComponent,
                      String aStateData)
    {
      if (aComponent != null && aComponent.isEnabled ())
      {
      MauiEvent theEvent = aComponent.createEvent (aStateData);

      //if (!theEvent.isConsumed())
      //{
        if (theEvent instanceof MActionEvent)
        {
          aComponent.dispatchActionEvent ((MActionEvent) theEvent);
        }
      //}
    }
  }
 
  // ---------------------------------------------------------------------------
}

// =============================================================================
// Copyright (c) 1999 Bitmovers Communications Inc.                          eof
TOP

Related Classes of com.bitmovers.maui.engine.httpserver.HTTPEventTranslator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.