Package com.bitmovers.maui.engine

Source Code of com.bitmovers.maui.engine.ApplicationManager

// =============================================================================
// CHANGELOG:
//++ 181 MW 2001.08.09
// Added the getAllApplicationNames() which returns a vector of all the names
// of the applications in the current Maui Environment.
// =============================================================================


package com.bitmovers.maui.engine;

import java.util.Properties;
import java.util.Hashtable;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Map;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipFile;
import java.io.File;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.FilenameFilter;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URL;
import sun.misc.URLClassPath;
import sun.misc.Resource;
import com.bitmovers.maui.profiler.Profiler;
import com.bitmovers.maui.MauiApplication;
import com.bitmovers.maui.I_SiteInitializer;
import com.bitmovers.maui.engine.resourcemanager.*;
import com.bitmovers.maui.MauiRuntimeEngine;
import com.bitmovers.maui.components.MDesktop;
import com.bitmovers.maui.engine.httpserver.HTTPSession;
import com.bitmovers.maui.engine.httpserver.I_SessionListener;
import com.bitmovers.maui.engine.httpserver.SessionEvent;
import com.bitmovers.maui.engine.logmanager.*;
import com.bitmovers.utilities.PropertiesLoader;


// ======================================================================
// CLASS: ApplicationManager                   (c) 2001 Bitmovers Systems
// ======================================================================

/** This object has two primary functions.  Firstly, during
  * initialization, this manager will scan jar files within the Maui
  * applications folder for <code>MauiApplication</code> classes.  It
  * then creates objects which represent each of these applications.
  *
  * During runtime, the ApplicationManager creates MauiApplication
  * objects on demand.
  *
  */
 
public class ApplicationManager
  implements I_SessionListener,
         Runnable
{
 
 
  //
  //  If the ClassLoader is shared, then a common will be created here, and used
  //  by all of the ApplicationSuites.
  //
  protected MauiClassLoader extensionsClassLoader = new MauiClassLoader ();
  protected ServerConfigurationManager scm;
  protected String applicationLocation;
  protected String workLocation;
  protected String extensionLocation;
  protected FolderInformation folderInformation = null;
  protected boolean autoReload = false;
  protected Vector extensionsLoaded = new Vector ();
  //protected Hashtable applications = new Hashtable (20);
  //protected Hashtable applicationNames = new Hashtable (20);
 
  private static final ApplicationManager am = new ApplicationManager ();
 
  private String defaultApplicationClassName;
  private String defaultFolder;
  private FolderInformation defaultFolderInformation;
  private String internalApplicationClassName;
  private boolean initDone = false;
  private Map folders = new TreeMap ();
  private Hashtable sessions = new Hashtable ();
  private Hashtable initializers = new Hashtable (5);
  private int applicationScanTime;
  private boolean firstTime = true;
 
  private Vector listeners = new Vector ();
  private Hashtable suiteReferences = new Hashtable (10);
  private Hashtable lowerCase = new Hashtable (50);
 
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // INNER CLASS: SessionInformation
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
  /** This inner class contains information about application/session cross
    * references.
    *
    */
   
  class SessionInformation
  {
   
   
    protected final Vector sessions = new Vector ();
    protected final MauiApplication mauiApplication;
   
   
    // --------------------------------------------------------------------
    // CONSTRUCTOR
    // --------------------------------------------------------------------
   
    SessionInformation (MauiApplication aMauiApplication)
    {
      mauiApplication = aMauiApplication;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getMauiApplication
    // --------------------------------------------------------------------
   
    protected MauiApplication getMauiApplication ()
    {
      return mauiApplication;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getSessions
    // --------------------------------------------------------------------
   
    protected Vector getSessions ()
    {
      return sessions;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: toArray
    // --------------------------------------------------------------------
   
    protected HTTPSession [] toArray ()
    {
      HTTPSession [] retVal = new HTTPSession [sessions.size ()];
      Object [] theSessions = sessions.toArray ();
      for (int i = 0; i < theSessions.length; i++)
      {
        retVal [i] = (HTTPSession) theSessions [i];
      }
      return retVal;
    }
   
    /**
    * Return a count of the number of sessions in the vector
    *
    * @return The number of sessions running this application
    */
    public int size ()
    {
      return sessions.size ();
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: isEmpty
    // --------------------------------------------------------------------
   
    protected boolean isEmpty ()
    {
      return (sessions.size () == 0);
    }
     
   
    // --------------------------------------------------------------------
    // METHOD: addSession
    // --------------------------------------------------------------------
   
    protected void addSession (HTTPSession aSession)
    {
      if (!sessions.contains (aSession))
      {
        sessions.addElement (aSession);
      }
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: removeSession
    // --------------------------------------------------------------------
   
    protected void removeSession (HTTPSession aSession)
    {
      if (sessions.contains (aSession))
      {
        sessions.removeElement (aSession);
      }
    }
   
   
  }
 
 
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // INNER CLASS: FolderInformation
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
  /** This inner class is used for grouping application suites by folder.
    * ApplicationSuites can be grouped under folders
    *
    */
   
  class FolderInformation
  {
    protected final String folder;
    protected final TreeMap suites = new TreeMap ();
    protected final TreeMap applications = new TreeMap ();
    protected final Map applicationNames = new TreeMap ();
   
   
    // --------------------------------------------------------------------
    // CONSTRUCTOR
    // --------------------------------------------------------------------
   
    protected FolderInformation (String aFolder)
    {
      folder = aFolder;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: addApplicationSuite
    // --------------------------------------------------------------------
   
    protected void addApplicationSuite (ApplicationSuite aApplicationSuite)
    {
      suites.put (aApplicationSuite.getName (), aApplicationSuite);
    }
   
    protected void removeApplicationSuite (ApplicationSuite aApplicationSuite)
    {
      suites.remove (aApplicationSuite.getName ());
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getSuite
    // --------------------------------------------------------------------
   
    /** Given an ApplicationSuite name get the ApplicationSuite name
      *
      * @param aSuiteName The application suite name
      *
      * @return The ApplicationSuite
      *
      */
     
    protected ApplicationSuite getSuite (String aSuiteName)
    {
      int theIndex = aSuiteName.indexOf (".jar");
      String theLookupKey = (theIndex == -1 ? aSuiteName: aSuiteName.substring (0, theIndex));
      TreeMap theSuites = suites;
      Object retVal = theSuites.get (theLookupKey);
      return (ApplicationSuite) retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getFolderName
    // --------------------------------------------------------------------
   
    protected String getFolderName ()
    {
      return folder;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getAllSuites
    // --------------------------------------------------------------------
   
    /** Get a sorted array of all of the ApplicationSuites within the folder
      *
      * @return An array of the ApplicationSuites contained within the folder
      *
      */
     
    protected ApplicationSuite [] getAllSuites ()
    {
      ApplicationSuite [] retVal = new ApplicationSuite [suites.size ()];
      Iterator theValues = suites.values ().iterator ();
      int i = 0;
      while (theValues.hasNext ())
      {
        Object theValue = theValues.next ();
        retVal [i++] = (ApplicationSuite) theValue;
      }
      return retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationSuiteNames
    // --------------------------------------------------------------------
   
    /** Get a sorted list of all of the application suites
      *
      * @return The sorted String array
      *
      */
     
    protected String [] getApplicationSuiteNames ()
    {
      ApplicationSuite [] theSuites = getAllSuites ();
      String [] retVal = new String [theSuites.length];
      for (int i = 0; i < retVal.length; i++)
      {
        retVal [i] = theSuites [i].getName ();
      }
      return retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: addApplication
    // --------------------------------------------------------------------
   
    protected void addApplication (MauiApplication aMauiApplication)
    {
      applications.put (aMauiApplication.getName(), aMauiApplication);
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationName
    // --------------------------------------------------------------------
   
    protected String getApplicationName (String aClassName)
    {
      return (String) applicationNames.get (getClassName (aClassName));
    }
 
   
    // --------------------------------------------------------------------
    // METHOD: getAllApplicationNames
    // --------------------------------------------------------------------
   
    protected String [] getAllApplicationNames ()
    {
      String [] retVal = new String [applications.size ()];
      Iterator theValues = applicationNames.values ().iterator ();
      int i = 0;
      while (theValues.hasNext ())
      {
        retVal [i++] = (String) theValues.next ();
      }
      return retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getWrapper
    // --------------------------------------------------------------------
   
    protected ApplicationClassWrapper getWrapper (String aClassName)
    {
      return (ApplicationClassWrapper) applications.get (getClassName (aClassName));
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getClassName
    // --------------------------------------------------------------------
   
    protected String getClassName (String aClassName)
    {
      int theSlash = aClassName.lastIndexOf ("/");
      return (theSlash == -1 ? aClassName : aClassName.substring (theSlash + 1));
    }
   
   
  }
 
 
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // INNER CLASS: ApplicationClassWrapper
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
  /** Inner class for maintaining cross-reference between a MauiApplication Class
    * and the ApplicationSuite it belongs to.
    *
    */
   
  class ApplicationClassWrapper
  {
   
   
    protected Class applicationClass;
    protected ApplicationSuite applicationSuite;
    protected Properties properties = null;
   
   
    // --------------------------------------------------------------------
    // CONSTRUCTOR
    // --------------------------------------------------------------------
   
    ApplicationClassWrapper (Class aApplicationClass,
                 ApplicationSuite aApplicationSuite,
                 Properties aProperties)
    {
      applicationClass = aApplicationClass;
      applicationSuite = aApplicationSuite;
      properties = aProperties;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationClass
    // --------------------------------------------------------------------
   
    protected Class getApplicationClass ()
    {
      return applicationClass;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationSuite
    // --------------------------------------------------------------------
   
    protected ApplicationSuite getApplicationSuite ()
    {
      return applicationSuite;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getProperties
    // --------------------------------------------------------------------
   
    protected Properties getProperties ()
    {
      return properties;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: createFolder
    // --------------------------------------------------------------------
   
    private void createFolder (String aLocation)
    {
      File theFile = new File (aLocation);
      if (!theFile.exists ())
      {
        theFile.mkdirs ();
      }
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: instantiateApplication
    // --------------------------------------------------------------------
   
    /** Instantiate the application
      *
      * @param aInitializer The I_ApplicationInitializer callback object
      *
      * @return The instantiated application
      *
      */
     
    protected MauiApplication instantiateApplication (I_ApplicationInitializer aInitializer)
      throws NoSuchMethodException,
           SecurityException,
           InstantiationException,
           InvocationTargetException,
           IllegalAccessException
    {
      Constructor theConstructor = applicationClass.getConstructor (new Class [] {Object.class});
      return (MauiApplication) theConstructor.newInstance (new Object [] {aInitializer});
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: createWorkSpace
    // --------------------------------------------------------------------
   
    /** Create the work space for this application
      *
      * @param aApplicationClassWrapper The ApplicationClassWrapper which describes the application
      *
      */
     
    protected void createWorkSpace ()
    {
      createFolder (properties.getProperty (MauiApplication.MAUI_USER_DIR));
    }
   
   
  }
 
 
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // INNER CLASS: ApplicationSuite
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 
  /** This class is a representation of a single jar file.  Since it is possible for
    * several MauiApplication classes to be contained within a single jar file, this
    * functions largely as a placeholder to group these classes together.
    *
    */
   
  class ApplicationSuite
  {
   
   
    protected MauiClassLoader classLoader;
    protected String name;
    protected String location;
    protected String fullyQualifiedLocation;
    protected TreeMap suiteApplications = new TreeMap ();
    protected Properties suiteProperties = null;
    private long lastModified;
    private String jarFileName;
    private FolderInformation folderInformation;
    private boolean firstLoad = true;
   
   
    // --------------------------------------------------------------------
    // CONSTRUCTOR
    // --------------------------------------------------------------------
   
    ApplicationSuite (final String aLocation,
              final String aJarFileName,
              final FolderInformation aFolderInformation)
      throws IOException
    {
      location = aLocation;
      int thePeriod = aJarFileName.indexOf (".jar");
      name = aJarFileName;
      jarFileName = aJarFileName;
      folderInformation = aFolderInformation;
      if (thePeriod != -1)
      {
        name = name.substring (0, thePeriod)// Remove ".jar"
        getFullyQualifiedLocation ();
        load ();
      }
    }
   
    protected void reload ()
      throws IOException
    {
      unload ();
      load ();
    }
   
    protected void unload ()
    {
      suiteProperties = null;
      Object [] theSuiteApplications = suiteApplications.values ().toArray ();
      for (int i = 0; i < theSuiteApplications.length; i++)
      {
        mauiApplicationAction ((Class) theSuiteApplications [i], false);
      }
      ResourceManager.getInstance ().removeResourceFile (fullyQualifiedLocation + File.separator + name + ".jar");
      /*try
      {
        classLoader.removeJarFile (new File (getFullyQualifiedLocation (), jarFileName));
      }
      catch (IOException e)
      {
        System.err.println ("[ApplicationSuite] unload exception: " + e);
      }*/
    }
   
    protected void load ()
      throws IOException
    {
      //
      //  Use either a private ClassLoader, or the shared one
      //
      classLoader = new MauiClassLoader (extensionsClassLoader);
     
      //
      // Look for suite level properties.  These are properties to be applied to all
      //  applications objects which originate from this application suite.
      //
      suiteProperties = scm.loadPropertiesFromFolder (getName (),
                              location,
                              null);
     
      //
      //  Add the jar file to the ClassLoader.  If this is shared then it means that this jar file
      //  will be added to a list of files.
      //
      File theJarFile = new File (fullyQualifiedLocation, jarFileName);
      lastModified = theJarFile.lastModified ();
      firstLoad = true;
      if (!firstTime)
      {
        try
        {
          ResourceManager.getInstance ().addResourceFile (new File (fullyQualifiedLocation + File.separator + name + ".jar"));
        }
        catch (Exception e)
        {
        }
      }
      classLoader.addJarFile (new File (fullyQualifiedLocation, jarFileName),
                  new I_JarCallback ()
                  {
                    public void mauiAppFound (Class aMauiApplication)
                    {
                      addMauiApplication (aMauiApplication);
                    }
                   
                    public void initializerFound (Class aInitializer)
                    {
                      addInitializer (aInitializer);
                    }
                  });
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationNames
    // --------------------------------------------------------------------
   
    protected String [] getApplicationNames ()
    {
      String [] retVal = new String [suiteApplications.size ()];
      Iterator theSuiteApplications = suiteApplications.values ().iterator ();
      int i = 0;
      while (theSuiteApplications.hasNext ())
      {
        retVal [i++] = ((Class) theSuiteApplications.next ()).getName ();
      }
      return retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getClassLoader
    // --------------------------------------------------------------------
   
    protected MauiClassLoader getClassLoader ()
    {
      return classLoader;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getProperties
    // --------------------------------------------------------------------
   
    protected Properties getProperties ()
    {
      return suiteProperties;
    }
   
    /**
    * Get the last modified date for jar file
    *
    * @return The last modified date
    */
    protected long getLastModified ()
    {
      return lastModified;
    }
   
    /**
    * Check if the latest jar file in the folder is out of date
    *
    * @return Boolean indicating if it is out of date or not
    */
    protected boolean isOutOfDate ()
    {
      File theFile = new File (fullyQualifiedLocation, jarFileName);
      return (lastModified < theFile.lastModified ());
    }
   
    protected void setOutOfDate ()
    {
      lastModified = (long) 0;
    }
   
    // --------------------------------------------------------------------
    // METHOD: loadPropertiesFromResource
    // --------------------------------------------------------------------
   
    private boolean loadPropertiesFromResource (String aResourceName, Properties aProperties)
      throws IOException
    {
      InputStream thePropertiesStream = getClassLoader ().findResourceStream (aResourceName + ".properties");
      if (thePropertiesStream != null)
      {
        aProperties.load (thePropertiesStream);
        try
        {
          thePropertiesStream.close ();
        }
        catch (IOException e)
        {
        }
      }
      return (thePropertiesStream == null ? false : true);
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getApplicationProperties
    // --------------------------------------------------------------------
   
    private Properties getApplicationProperties (final String aAppName, final String aClassName)
    {
      Properties retVal = (Properties) getProperties ().clone ();
      int thePeriod = aAppName.indexOf (".");
      if (thePeriod != -1)
      {
        scm.loadPropertiesFromFolder (aAppName.substring (0, thePeriod), location, retVal);
      }
      scm.loadPropertiesFromFolder (aAppName, location, retVal);
      scm.loadPropertiesFromFolder (aClassName, location, retVal);
      thePeriod = aClassName.lastIndexOf (".");
      scm.loadPropertiesFromFolder ((thePeriod != -1 ?
                                          aClassName.substring (thePeriod + 1) :
                                          aClassName),
                                    location,
                                    retVal);
      try
      {
        if (!loadPropertiesFromResource (aAppName, retVal))
        {
          try
          {
            loadPropertiesFromResource (aClassName, retVal);
          }
          catch (IOException e)
          {
          }
        }
      }
      catch (IOException e)
      {
        try
        {
          loadPropertiesFromResource (aClassName, retVal);
        }
        catch (IOException e2)
        {
        }
      }
      return retVal;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: addMauiApplication
    // --------------------------------------------------------------------
   
    /** Add a MauiApplication to the ApplicationSuite.  This method also
      * takes responsible for cataloguing the MauiApplication. This
      * includes determining the shortest, unambiguous name for each
      * application.
      *
      * @param aMauiApplication The MauiApplication Class
      *
      */
     
    protected void addMauiApplication (final Class aMauiApplication)
    {
      mauiApplicationAction (aMauiApplication, true);
    }
   
    protected void mauiApplicationAction (final Class aMauiApplication, boolean aAdd)
    {
      String theAppClassName = aMauiApplication.getName ();
      String theFullClassName = theAppClassName;
     
      int thePeriod = theAppClassName.lastIndexOf (".");
      if (thePeriod != -1)
      {
        theAppClassName = theAppClassName.substring (thePeriod + 1);
      }
     
      String theAppName = getName () + "." + theAppClassName;
      ApplicationClassWrapper theWrapper;
     
      if (firstLoad)
      {
        try
        {
          loadPropertiesFromResource (getName (), suiteProperties);
        }
        catch (IOException e)
        {
        }
        firstLoad = false;
      }
     
      if (aAdd)
      {
        suiteApplications.put (theAppClassName,
                     aMauiApplication);
        theWrapper = new ApplicationClassWrapper (aMauiApplication,
                                  ApplicationSuite.this,
                                  getApplicationProperties (theAppName,
                                                theFullClassName));
        StringBuffer theWorkLocation = new StringBuffer (workLocation);
        if (!location.equals (""))
        {
          theWorkLocation.append (File.separator);
          theWorkLocation.append (location);
        }
        theWorkLocation.append (File.separator);
        String theClassName = theWrapper.getApplicationClass ().getName ();
        theWorkLocation.append (theClassName.replace ('.', File.separatorChar));
        Properties theProperties = theWrapper.getProperties ();
        String theWorkFolderName = theWorkLocation.toString ();
        theProperties.put (MauiApplication.MAUI_USER_DIR, theWorkFolderName);
        int theDot = theClassName.lastIndexOf ('.');
        theClassName = (theDot == -1 ? theClassName : theClassName.substring (theDot + 1));
        int theSlash = theClassName.lastIndexOf (File.separatorChar);
        theClassName = (theSlash == -1 ? theClassName : theClassName.substring (theSlash + 1));
        theProperties.put ("maui." + theClassName + ".working.folder", theWorkFolderName);
        if (folderInformation.applications.get (theAppClassName) != null)
        {
          System.err.println ("[Application Manager] MauiApplication " + theFullClassName +
                    " was already defined in " + theWrapper.getApplicationSuite ().getName ());
        }
        else
        {
          folderInformation.applications.put (theAppClassName, theWrapper);
          folderInformation.applicationNames.put (theAppClassName, theFullClassName);
        }
       
        //Properties theProperties = (Properties) getProperties ().clone ();
        folderInformation.applications.put (theAppName, theWrapper);
        folderInformation.applicationNames.put (theAppName, theFullClassName);
        folderInformation.applications.put (theFullClassName, theWrapper);
       
        if (theFullClassName.equals (defaultApplicationClassName))
        {
          String theDefaultName = "default." + theAppClassName;
          folderInformation.applications.put (theDefaultName, theWrapper);
          folderInformation.applicationNames.put (theDefaultName, theFullClassName);
        }
       
      }
      else
      {
        suiteApplications.remove (theAppClassName);
        theWrapper = getApplicationClassWrapper (location, theAppClassName);
        folderInformation.applications.remove (theAppClassName);
        folderInformation.applicationNames.remove (theAppClassName);
        folderInformation.applications.remove (theAppName);
        folderInformation.applicationNames.remove (theAppName);
        folderInformation.applications.remove (theFullClassName);
       
        if (theFullClassName.equals (defaultApplicationClassName))
        {
          String theDefaultName = "default." + theAppClassName;
          folderInformation.applications.remove (theDefaultName);
          folderInformation.applicationNames.remove (theDefaultName);
        }
       
      }
     
                 
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: addInitializer
    // --------------------------------------------------------------------
   
    protected void addInitializer (Class aInitializer)
    {
      initializers.put (aInitializer.getName (), aInitializer);
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getName
    // --------------------------------------------------------------------
   
    protected String getName ()
    {
      return name;
    }
   
    public String getJarFileName ()
    {
      return jarFileName;
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getLocation
    // --------------------------------------------------------------------
   
    protected String getLocation ()
    {
      return location;
    }
   
    /**
    * Get the fully qualified location
    *
    * @return The fully qualified location
    */
    protected void getFullyQualifiedLocation ()
    {
      StringBuffer theLocation = new StringBuffer (applicationLocation);
      if (location != null && location.length () > 0)
      {
        theLocation.append (File.separator);
        theLocation.append (location);
      }
      fullyQualifiedLocation =  theLocation.toString ();
    }
   
   
    // --------------------------------------------------------------------
    // METHOD: getFullName
    // --------------------------------------------------------------------
   
    protected String getFullName ()
    {
      return (location.length () > 0 ? location + "/" : "") + name;
    }
  }

  // ----------------------------------------------------------------------
  // STATIC METHOD: getInstance
  // ----------------------------------------------------------------------
 
  public static ApplicationManager getInstance ()
  {
    return am;
  }
 
  // --------------------------------------------------------------------
  // METHOD: addApplicationSuite
  // --------------------------------------------------------------------
  public ClassLoader getExtensionsClassLoader ()
  {
    return extensionsClassLoader;
  }
 
 
 
  /*
  // --------------------------------------------------------------------
  // METHOD: addApplicationSuite
  // --------------------------------------------------------------------
 
  private void addApplicationSuite (String aSuiteName, String aClassName)
  {
    try
    {
      ApplicationSuite theSuite = new ApplicationSuite (aSuiteName);  //  Load up the default.
      theSuite.addMauiApplication (Class.forName (aClassName));
      suites.put (theSuite.getName (), theSuite);
    }
    catch (Exception e)
    {
    }
  }*/
 
  private String [] getJarFiles (File aLocation)
  {
    String [] theJarFiles =
      aLocation.list (new FilenameFilter ()
                {
                  public boolean accept (File aDir, String aName)
                  {
                    return aName.toLowerCase ().endsWith (".jar");
                  }
                });
    return theJarFiles;
  }
 
  public void addApplicationManagerListener (ApplicationManagerListener aListener)
  {
    if (!listeners.contains (aListener))
    {
      listeners.addElement (aListener);
    }
  }
 
  public void removeApplicationManagerListener (ApplicationManagerListener aListener)
  {
    if (listeners.contains (aListener))
    {
      listeners.removeElement (aListener);
    }
  }
 
  protected void notifyManagerListeners (Object aObjectSource, String aName, int aAction)
  {
    if (listeners.size () > 0)
    {
      ApplicationManagerEvent theEvent =
        new ApplicationManagerEvent (aObjectSource, aName);
      Enumeration theListeners = listeners.elements ();
      while (theListeners.hasMoreElements ())
      {
        ApplicationManagerListener theListener =
          (ApplicationManagerListener) theListeners.nextElement ();
        switch (aAction)
        {
          case (ApplicationManagerListener.LOADED) :
            theListener.applicationSuiteLoaded (theEvent);
            break;
           
          case (ApplicationManagerListener.UNLOADED) :
            theListener.applicationSuiteUnloaded (theEvent);
            break;

          case (ApplicationManagerListener.RELOADED) :
            theListener.applicationSuiteReloaded (theEvent);
            break;

          case (ApplicationManagerListener.EXTENSIONS) :
            theListener.extensionsLoaded (theEvent);
            break;
        }

      }
    }
  }
   
  /**
  * Scan the extensions folder, and add any extensions that haven't already been defined
  */
  private void scanForExtensions ()
  {
    extensionsClassLoader.setLoadAllProperties (true);
    File theExtensionsFolder = new File (extensionLocation);
    if (!theExtensionsFolder.exists ())
    {
      theExtensionsFolder.mkdir ();
    }
    if (theExtensionsFolder.exists () &&
      theExtensionsFolder.isDirectory ())
    {
      boolean theLoadAllExtensions = false;
      String [] theJarFiles = getJarFiles (theExtensionsFolder);
      if (theJarFiles.length > 0)
      {
        for (int i = 0; i < theJarFiles.length; i++)
        {
          if (!extensionsLoaded.contains (theJarFiles [i]))
          {
            try
            {
              extensionsClassLoader.addJarFile (new File (theExtensionsFolder, theJarFiles [i]), null);
              theLoadAllExtensions = true;
            }
            catch (IOException e)
            {
            }
            extensionsLoaded.addElement (theJarFiles [i]);
          }
        }
        if (autoReload && theLoadAllExtensions)
        {
          extensionsClassLoader.loadAllExtensions ();
        }
      }
    }
   
    notifyManagerListeners (this,
                "Extensions",
                ApplicationManagerListener.EXTENSIONS);
  }
 
  /**
  * Go through the list of ApplicationSuite objects and prune out any that no longer
  * physically exist.
  *
  * @param aFolder The FolderInformation object
  * @param aJarFiles Up-to-date list of jar file names found within the folder
  */
  private void scanForDeletedSuites (FolderInformation aFolder, String [] aJarFiles)
  {
    Vector theJarFiles = new Vector (aJarFiles.length);
    for (int i = 0; i < aJarFiles.length; i++)
    {
      int thePeriod = aJarFiles [i].toLowerCase ().indexOf (".jar");
      if (thePeriod != -1)
      {
        theJarFiles.addElement (aJarFiles [i].substring (0, thePeriod));
      }
    }
   
    Object [] theSuites = aFolder.suites.values ().toArray ();
    for (int i = 0; i < theSuites.length; i++)
    {
      ApplicationSuite theSuite = (ApplicationSuite) theSuites [i];
      if (!theJarFiles.contains (theSuite.getName ()))
      {
        theSuite.setOutOfDate ();
        try
        {
          isOutOfDate (theSuite, true);
        }
        catch (IOException e)
        {
        }
        aFolder.removeApplicationSuite (theSuite);
        notifyManagerListeners (theSuite, theSuite.getName (), ApplicationManagerListener.UNLOADED);
      }
    }
  }
 
  /**
  * Go through the list of FolderInformation objects and prune out any that
  * no longer physically exist.
  *
  * @param aLocation The folder being scanned
  * @param aFolders Up-to-date list of folders for this location
  */
  private void scanForDeletedFolders (File aLocation, File [] aFolders)
  {
    FolderInformation [] theFolders = getFolders ();
    HashMap theFoldersMap = new HashMap (theFolders.length);
    String theLocationName = aLocation.getName ();
    if (theLocationName.startsWith (applicationLocation))
    {
      theLocationName = theLocationName.substring (applicationLocation.length ());
    }
    for (int i = 0; i < theFolders.length; i++)
    {
      String theFolderName = theFolders [i].getFolderName ();
      if (theFolderName.startsWith (theLocationName))
      {
        theFolderName = theFolderName.substring (theLocationName.length ());
        if (theFolderName.length () > 0)
        {
          theFoldersMap.put (theFolderName, theFolders [i]);
        }
      }
    }
   
    for (int i = 0; i < aFolders.length; i++)
    {
      theFoldersMap.remove (aFolders [i].getName ());
    }
   
    Object [] theRemainder = theFoldersMap.values ().toArray ();
    for (int i = 0; i < theRemainder.length; i++)
    {
      scanForDeletedSuites ((FolderInformation) theRemainder [i], new String [0]);
      folders.remove (((FolderInformation) theRemainder [i]).getFolderName ());
    }
   
  }
 
  // ----------------------------------------------------------------------
  // METHOD: buildApplicationSuites
  // ----------------------------------------------------------------------
 
  /** This method takes responsibility for loading all of the ApplicationSuites (ie. jar files) within a particular
    * folder.  This includes locating all jar files within the folder, scanning each jar file for MauiApplications (ie.
    * classes which extend the MauiApplicaition class), creating descriptions for them (ApplicationClassWrappers), and
    * cataloguing them.
    *
    * @param aLocation The folder to scan
    *
    */
   
  private void buildApplicationSuites (final File aLocation)
  {
    //addApplicationSuite ("default", defaultApplicationClassName);
    //addApplicationSuite ("maui", internalApplicationClassName);
   
    if (aLocation.exists ())
    {
      String theUseLocation = aLocation.toString ();
      int theIndex = theUseLocation.indexOf (applicationLocation) + applicationLocation.length ();
      theUseLocation = theUseLocation.substring (theIndex);
      if (theUseLocation.startsWith (File.separator))
      {
        theUseLocation = theUseLocation.substring (1);
      }
     
      String [] theJarFiles = getJarFiles (aLocation);
      String theFolderLocation = theUseLocation.replace (File.separatorChar, '/');
      folderInformation = (FolderInformation) folders.get (theFolderLocation);
      if (folderInformation == null)
      {
        folderInformation = new FolderInformation (theUseLocation);
        folders.put (theFolderLocation, folderInformation);
      }
     
      String theJarFile;
      if (theJarFiles.length > 0)
      {
        TreeMap theSuites = folderInformation.suites;
       
        for (int i = 0; i < theJarFiles.length; i++)
        {
          try
          {
            int thePeriod = theJarFiles [i].indexOf (".jar");
            if (thePeriod != -1)
            {
              String theSuiteName = theJarFiles [i].substring (0, thePeriod);
              ApplicationSuite theSuite;
              if ((theSuite = (ApplicationSuite) theSuites.get (theSuiteName)) == null)
              {
                theSuite = new ApplicationSuite (theUseLocation,
                                   theJarFiles [i],
                                   folderInformation);
                System.out.println (new InfoString ("Loading " + theSuite.getFullName () + "."));
                theSuites.put (theSuite.getName (), theSuite);
                notifyManagerListeners (theSuite, theSuite.getFullName (), ApplicationManagerListener.LOADED);
              }
              else if (theSuite.isOutOfDate ());
              {
                isOutOfDate (theSuite, false);
              }
            }
          }
          catch (IOException e)
          {
            System.err.println (new ErrorString ("[ApplicationManager] Exception adding jar file " + theJarFiles [i] + " : " + e));
          }
        }

        scanForDeletedSuites (folderInformation, theJarFiles);       
      }
     
      File [] theFolders =
        aLocation.listFiles (new FileFilter ()
                    {
                      public boolean accept (File aFile)
                      {
                        return (aFile.isDirectory () &&
                            !aFile.getName ().startsWith (".") &&
                            !aFile.getName ().toLowerCase ().endsWith ("resource.frk"));
                      }
                    });
      scanForDeletedFolders (aLocation, theFolders);
      for (int i = 0; i < theFolders.length; i++)
      {
        buildApplicationSuites (theFolders [i]);
      }
    }   
  }
   
 
  // ----------------------------------------------------------------------
  // METHOD: siteInitialization
  // ----------------------------------------------------------------------
 
  /** Perform site specific initialization
    *
    * @param aConfigurationProperties The properties Hashtable
    *
    */
   
  private void siteInitialization (final Properties aConfigurationProperties)
  {
    TreeMap theSortedMap = new TreeMap ();
    ServerConfigurationManager theSCM = ServerConfigurationManager.getInstance ();
   
    Enumeration theKeys = aConfigurationProperties.keys ();
    while (theKeys.hasMoreElements ())
    {
      String theKey = (String) theKeys.nextElement ();
      if (theKey.startsWith (theSCM.MAUI_SITE_INITIALIZER))
      {
        theSortedMap.put (theKey, aConfigurationProperties.getProperty (theKey));
      }
    }
   
    if (theSortedMap.size () > 0)
    {
      Iterator theValues = theSortedMap.values ().iterator ();
      int i = 0;
      while (theValues.hasNext ())
      {
        String theClass = (String) theValues.next ();
        try
        {
          I_SiteInitializer theInitializer = getSiteInitializer (theClass);
          if (theInitializer != null)
          {
            theInitializer.initializeSite (theSCM.getGlobalProperties ());
          }
          else
          {
            System.err.println ("[ServerConfigurationManager] - Expected site specific initializer class not found : " +
                      theClass);
          }
        }
        catch (Exception e)
        {
          System.err.println ("[ServerConfigurationManager] - Site specific initializer exception: (class: " +
                    theClass + ") (exception: " + e + ")");
        }
      }
    }
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: initialize
  // ----------------------------------------------------------------------
 
  /** Initialize the ApplicationManager.  Get some properties that the ApplicationManager uses:
    * <pre>
    * - the location of the application folder
    * - a boolean indicating if a shared ClassLoader should be used or not
    * - the default MauiApplication name
    * </pre>
    *
    */
   
  public void initialize ()
  {
    if (!initDone)
    {
      int theReference = Profiler.start (MauiRuntimeEngine.SOURCE_APPLICATION_MANAGER,
                         MauiRuntimeEngine.ACTION_INITIALIZE);
      //
      //  Do some initialization
      //
      scm = ServerConfigurationManager.getInstance ();
      applicationLocation = scm.getProperty (scm.MAUI_APPLICATION_LOCATION);
      workLocation = scm.getProperty (scm.MAUI_APPLICATION_WORK_SPACE_LOCATION);
      extensionLocation = scm.getProperty (scm.MAUI_EXTENSION_LOCATION);
      autoReload = scm.getProperty (scm.MAUI_AUTO_RELOAD).equalsIgnoreCase ("true");
         
      defaultFolder = scm.getProperty (scm.MAUI_DEFAULT_FOLDER);
      defaultFolderInformation = new FolderInformation (defaultFolder);
      folders.put ("", defaultFolderInformation);
      folders.put (defaultFolder, defaultFolderInformation);
      defaultApplicationClassName = getMauiApplicationClassName (scm.getProperty (scm.MAUI_DEFAULT_APPLICATION));
      internalApplicationClassName = getMauiApplicationClassName (scm.getProperty (scm.MAUI_INTERNAL_APPLICATION));
      //if (isSharedClassLoader)
      //{
        //sharedClassLoader = new MauiClassLoader ();
      //}
     
      System.out.println (new DebugString("[ApplicationManager] - Starting..."));
      defaultFolderInformation.applicationNames.put ("default", defaultApplicationClassName);
      if (autoReload)
      {
        try
        {
          applicationScanTime = Integer.parseInt (scm.getProperty (scm.MAUI_APPLICATION_SCAN_TIME)) * 1000;
        }
        catch (NumberFormatException e)
        {
          applicationScanTime = 20000;
        }
       
      }
     
      addApplicationManagerListener (new ApplicationManagerListener ()
        {
          public void extensionsLoaded (ApplicationManagerEvent aEvent)
          {
          }
         
          public void applicationSuiteLoaded (ApplicationManagerEvent aEvent)
          {
            catalogueSuite ((ApplicationSuite) aEvent.getSource ());
          }
         
          public void applicationSuiteUnloaded (ApplicationManagerEvent aEvent)
          {
            uncatalogueSuite ((ApplicationSuite) aEvent.getSource ());
          }
         
          public void applicationSuiteReloaded (ApplicationManagerEvent aEvent)
          {
            ApplicationSuite theSuite = (ApplicationSuite) aEvent.getSource ();
            uncatalogueSuite (theSuite);
            catalogueSuite (theSuite);
          }
        });
      new Thread (this, "AppMgr - AutoLoad").start ();
      /*synchronized (this)
      {
        try
        {
          wait ();
        }
        catch (InterruptedException e)
        {
        }
      }*/
      /*if (extensionLocation != null)
      {
        scanForExtensions ();
      }
     
      if (applicationLocation != null)
      {
        buildApplicationSuites (new File (applicationLocation));
      }
     
      siteInitialization (scm.getProperties ());*/
     
      //
      //  Listen for session level events (creation, deletion, application addition)
      //
      HTTPSession.addSessionListener (this);
      initDone = true;
     
      System.out.println (new DebugString("[ApplicationManager] - Started."));
      Profiler.finish (theReference, null);
    }
  }
 
  // ----------------------------------------------------------------------
  // METHOD: catalogueSuite
  // ----------------------------------------------------------------------
 
  private void addName (String aName, Vector aTargetVector)
  {
    lowerCase.put (aName, aName);
    lowerCase.put (aName.toLowerCase (), aName);
    aTargetVector.addElement (aName);
    aTargetVector.addElement(aName.toLowerCase ());
  }
 
  /** Do lowercase cataloguing of applications withing an ApplicationSuite
    *
    * @param aApplicationSuite The ApplicationSuite
    */
  public void catalogueSuite (ApplicationSuite aApplicationSuite)
  {
    uncatalogueSuite (aApplicationSuite);
   
    Vector theLowerCaseNames = new Vector (20);
    suiteReferences.put (aApplicationSuite.getFullName (), theLowerCaseNames);
    String theLocation = aApplicationSuite.getLocation ();
    String [] theApplicationNames = aApplicationSuite.getApplicationNames ();
    StringBuffer theName = new StringBuffer ();
    for (int i = 0; i < theApplicationNames.length; i++)
    {
      if (theLocation != null &&
        theLocation.length () > 0)
      {
        theName.append (theLocation);
        theName.append ("/");
      }
      int theLength = theName.length ();
      if (theLength != 0)
      {
        theName.append (theApplicationNames [i]);
        addName (theName.toString (), theLowerCaseNames);
      }
      addName (theApplicationNames [i], theLowerCaseNames);
      int theIndex = theApplicationNames [i].lastIndexOf (".");
      if (theIndex != -1)
      {
        String theShortName = theApplicationNames [i].substring (theIndex + 1);
        addName (theShortName, theLowerCaseNames);
        addName (aApplicationSuite.getName () + "." + theShortName, theLowerCaseNames);
        if (theLength != 0)
        {
          theName.setLength (theLength);
          theName.append (theShortName);
          addName (theName.toString (), theLowerCaseNames);
        }
      }
      else
      {
        addName (aApplicationSuite.getName () + "." + theApplicationNames [i], theLowerCaseNames);
      }
      theName.setLength (0);
   
  }
 
  // ----------------------------------------------------------------------
  // METHOD: catalogueSuite
  // ----------------------------------------------------------------------
 
  /** Uncatalogue all of the applications within the ApplicationSuite
    *
    * @param aApplicationSuite The ApplicationSuite
    */
  public void uncatalogueSuite (ApplicationSuite aApplicationSuite)
  {
    Vector theNames = (Vector) suiteReferences.remove (aApplicationSuite.getFullName ());
    if (theNames != null)
    {
      Enumeration theNamesEnumeration = theNames.elements ();
      String theName;
      while (theNamesEnumeration.hasMoreElements ())
      {
        theName = (String) theNamesEnumeration.nextElement ();
        lowerCase.remove (theName);
        lowerCase.remove (theName.toLowerCase ());
      }
    }
  }

  // ----------------------------------------------------------------------
  // METHOD: getFolders
  // ----------------------------------------------------------------------
 
  /** Get an array of all of the FolderInformation objects
    *
    *
    * @return The array of FolderInformation objects
    *
    */
   
  protected FolderInformation [] getFolders ()
  {
    Object [] theFolders = folders.values ().toArray ();
    FolderInformation [] retVal = new FolderInformation [theFolders.length];
    for (int i = 0; i < retVal.length; i++)
    {
      retVal [i] = (FolderInformation) theFolders [i];
    }
    return retVal;
  }
   

  // ----------------------------------------------------------------------
  // METHOD: getFolder
  // ----------------------------------------------------------------------
 
  /** Get a FolderInformation object.  This object contains information on the ApplicationSuites it contains.  This method
    * uses a class name of a MauiApplication, and retrieves the containing FolderInformation object
    *
    * @param aFolderName The name of the folder, or null to use the default
    *
    * @return The associated FolderInformation object
    *
    */
   
  protected FolderInformation getFolder (String aFolderName)
  {
    String theFolderName = new String (aFolderName);
    Object retVal = folders.get ((theFolderName == null ? "" : theFolderName));
    if (retVal == null)
    {
      int theSlash = theFolderName.lastIndexOf ("/");
      if (theSlash != -1)
      {
        theFolderName = theFolderName.substring (0, theSlash);
        retVal = folders.get (theFolderName);
      }
    }
   
    if (retVal != null && theFolderName.length () > 0)
    {
      //
      //  Make sure that a folder and file name aren't the same
      //
      File theFolder = new File (applicationLocation, theFolderName);
      if (theFolder.isDirectory ())
      {
        final String theFinalName = theFolderName;
        //
        //  This might be a directory, or it might be a file name
        //
        theFolder = theFolder.getParentFile ();
        if (theFolder != null)
        {
          String [] theFiles =
            theFolder.list (new FilenameFilter ()
                      {
                        public boolean accept (File aDir, String aName)
                        {
                          boolean retVal2 = false;
                          if (aName.startsWith (theFinalName))
                          {
                            retVal2 = !new File (aDir, aName).isDirectory ();
                          }
                          return retVal2;
                        }
                      });
          if (theFiles.length != 0)
          {
            retVal = null;
          }
        }
      }
    }
    return (FolderInformation) (retVal == null ? folders.get ("") : retVal);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getFolderName
  // ----------------------------------------------------------------------
 
  /** Get the folder name
    *
    * @param aClassName The name of the class
    *
    * @return The associated folder name
    *
    */
   
  public String getFolderName (String aClassName)
  {
    FolderInformation theFolder = getFolder (aClassName);
    return theFolder.getFolderName ();
  }
 
  /**
    * Get the ApplicationSuite for an application
    *
    * @param aMauiApplication The MauiApplication to check
    */
  protected ApplicationSuite getApplicationSuite (MauiApplication aMauiApplication)
  {
    String theApplicationName = aMauiApplication.getApplicationName ();
    String theFolderName = aMauiApplication.getFolderName ();
    ApplicationClassWrapper theWrapper = getApplicationClassWrapper (theFolderName, theApplicationName);
    return theWrapper.getApplicationSuite ();
  }
 
 
  public void setOutOfDate (MauiApplication aMauiApplication)
  {
    ApplicationSuite theSuite = getApplicationSuite (aMauiApplication);
    theSuite.setOutOfDate ();
  }
 
  /** Check for the application suite being out of date
    *
    * @return Boolean indicating if the application suite is out of date
    */
  public boolean isOutOfDate (MauiApplication aMauiApplication)
    throws IOException
  {
    boolean retVal = false;
   
    if (autoReload)
    {
      ApplicationSuite theSuite = getApplicationSuite (aMauiApplication);
      retVal = isOutOfDate (theSuite, false);
    }
   
    return retVal;
   
  }
 
  /**
  * Check for an ApplicationSuite being out of date
  *
  * @param aApplicationSuite The ApplicationSuite to check
  * @param aUnloadOnly Boolean indicating if this is unload only
  *
  * @param Boolean indicating if the suite is out of date or not
  */
  protected boolean isOutOfDate (ApplicationSuite  aApplicationSuite,
                   boolean aUnloadOnly)
    throws IOException
  {
    boolean retVal = false;
    if (aUnloadOnly || aApplicationSuite.isOutOfDate ())
    {
      retVal = true;
      String theFolderName = new String (aApplicationSuite.getLocation ());
      if (theFolderName.length () > 0)
      {
        theFolderName += "/";
      }
     
      MauiApplication [] theApplications = getRunningApplications ();
      for (int i = 0; i < theApplications.length; i++)
      {
        if (getApplicationSuite (theApplications [i]) == aApplicationSuite)
        {
          HTTPSession [] theSessions = getSessions (theApplications [i]);
          String theApplicationClass = theFolderName + theApplications [i].getClass ().getName ();
          for (int j = 0; j < theSessions.length; j++)
          {
            MauiApplication theApplication = theSessions [j].getCachedMauiApplication (theApplicationClass);
            if (theApplication != null)
            {
              theApplication.exit ();
              theSessions [j].removeApplication (theApplication);
              theApplication.setChainApplicationName (theFolderName + theApplication.getName ());
            }
          }
        }
      }
      StringBuffer theStatusMessage = new StringBuffer ((aUnloadOnly ? "Unloading " : "Reloading "));
      theStatusMessage.append (aApplicationSuite.getFullName ());
      theStatusMessage.append (".");
      System.out.println (new InfoString (theStatusMessage.toString ()));
      if (aUnloadOnly)
      {
        aApplicationSuite.unload ();
        notifyManagerListeners (aApplicationSuite,
                    aApplicationSuite.getFullName (),
                    ApplicationManagerListener.UNLOADED);
      }
      else
      {
        aApplicationSuite.reload ();
        notifyManagerListeners (aApplicationSuite,
                    aApplicationSuite.getFullName (),
                    ApplicationManagerListener.RELOADED);
      }
    }
    return retVal;
  }
 
  // ----------------------------------------------------------------------
  // METHOD: doAuthorizationCheck
  // ----------------------------------------------------------------------
 
  /** Check authorizations.  If any fail, then an appropriate
    * MauiApplication will be created.
    *
    * @param aSession The HTTPSession
    * @param aInitializer The I_ApplicationInitializer object
    */
  private MauiApplication doAuthorizationCheck (HTTPSession aSession,
                          I_ApplicationInitializer aInitializer)
  {
    MauiApplication retVal = null;
    AuthorizationManager theAuth = AuthorizationManager.getInstance ();
    if (!theAuth.isAuthorized (null, theAuth.AUTHORIZATION_SESSIONS))
    {
      retVal = new com.bitmovers.maui.engine.httpserver.SessionLimitExceeded (aInitializer);
    }
    else if (aSession.isServletBased () && !theAuth.isAuthorized (null, theAuth.AUTHORIZATION_SERVLET))
    {
      retVal = new com.bitmovers.maui.engine.servlet.NoServletSupport (aInitializer);
    }
    return retVal;
  }
 
  // ----------------------------------------------------------------------
  // METHOD: createMauiApplication
  // ----------------------------------------------------------------------
 
  /** Create a MauiApplication object.
    *
    * @param aRequestHeader Information from the request header
    * @param aCheckpoint A hashtable of checkpoint values set on the client side
    * @param aSession The session associated with the application being created
    * @param aAppName The class name of the MauiApplication to create
    * @param aCreateDefault Boolean indicating if a default application should be created.
    *
    * @return The initialized MauiApplication object
    *
    * @exception ClassNotFoundException This could happen if an invalid class name is passed in
    *
    */
   
  public MauiApplication createMauiApplication (final Map aRequestHeader,
                          final Hashtable aCheckpoint,
                          final HTTPSession aSession,
                          final String aClassName,
                          final boolean aCreateDefault)
    throws ClassNotFoundException,
         InstantiationException,
         IllegalAccessException,
         SecurityException,
         NoSuchMethodException,
         InvocationTargetException,
         IllegalAccessException
  {
    MauiApplication retVal = null;
    if (aClassName != null &&
      aClassName.trim ().length () > 0)
    {
     
      //FolderInformation theFolderInformation = getFolder (aClassName);
      //String theClassName = theFolderInformation.getClassName (aClassName);
      //theClassName = getMauiApplicationClassName (theClassName);
      //ApplicationClassWrapper theWrapper = (ApplicationClassWrapper) theFolderInformation.applications.get (theClassName);
      final ApplicationClassWrapper theWrapper = getApplicationClassWrapper (aClassName);
      if (theWrapper == null)
      {
        if (aCreateDefault)
        {
          String theLocation = (defaultFolder.trim ().length () == 0 ?
                        "" :
                        defaultFolder + "/");   
          retVal = createMauiApplication (aRequestHeader, aCheckpoint, aSession, theLocation + defaultApplicationClassName, false);
        }
        else
        {
          FolderInformation theFolderInformation = getFolder (aClassName);
          String theClassName = theFolderInformation.getClassName (aClassName);
          theClassName = getMauiApplicationClassName (theClassName);
          throw new ClassNotFoundException (theClassName);
        }
      }
      else
      {
        I_ApplicationInitializer theInitializer = new I_ApplicationInitializer ()
          {
            public void initializeApplication (MauiApplication aMauiApplication)
            {
             
              theWrapper.createWorkSpace ();
              ComponentManager.getInstance ().setApplication (aMauiApplication);
              ApplicationSuite theApplicationSuite = theWrapper.getApplicationSuite ();
              aMauiApplication.setApplicationSuiteName (theApplicationSuite.getName ());
              aMauiApplication.setApplicationName (aClassName);
              aMauiApplication.setChainApplicationName (aClassName);
              aMauiApplication.setShortName (getShortName (aClassName));
              aMauiApplication.setSession (aSession);
              aMauiApplication.setFolderName (theApplicationSuite.getLocation ());
              aMauiApplication.setInboundCookies (aCheckpoint);
              Properties theProperties = (Properties) theWrapper.getProperties ().clone ();
              aMauiApplication.setProperties (theProperties);
              String theProperty = theProperties.getProperty (scm.MAUI_APPLICATION_PREAMBLE);
              if (theProperty != null)
              {
                aMauiApplication.setPreamble (theProperty);
              }
             
              theProperty = theProperties.getProperty (scm.MAUI_APPLICATION_POSTAMBLE);
              if (theProperty != null)
              {
                aMauiApplication.setPostamble (theProperty);
              }
             
              theProperty = theProperties.getProperty (scm.MAUI_APPLICATION_BACKGROUND_COLOR);
              if (theProperty != null)
              {
                aMauiApplication.setBackgroundColor (theProperty);
              }
             
              theProperty = theProperties.getProperty (scm.MAUI_APPLICATION_BACKGROUND_IMAGE);
              if (theProperty != null)
              {
                aMauiApplication.setBackgroundImage (theProperty);
              }
             
              aMauiApplication.setDesktop (MDesktop.getInstance ());
              aMauiApplication.initialize (aRequestHeader);
            }
          };
        if ((retVal = doAuthorizationCheck (aSession, theInitializer)) == null)
        {
          retVal = theWrapper.instantiateApplication (theInitializer);
        }
        try
        {
          if (isOutOfDate (retVal))
          {
            retVal = createMauiApplication (aRequestHeader,
                            aCheckpoint,
                            aSession,
                            aClassName,
                            aCreateDefault);
          }
        }
        catch (IOException e)
        {
        }
      }
    }
    else if (aCreateDefault)
    {
      //
      //  Create a default application
      //
      retVal = createMauiApplication (aRequestHeader, aCheckpoint, aSession, defaultApplicationClassName, false);
    }
    else
    {
      throw new ClassNotFoundException ((aClassName == null ? defaultApplicationClassName : aClassName));
    }
   
    if (retVal != null)
    {
      retVal.staticInitialize ();
      if (aClassName != null && aClassName.equals (defaultApplicationClassName))
      {
        retVal.setDefaultApplication (true);
      }
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getDefaultApplicationClassName
  // ----------------------------------------------------------------------
 
  /** Get the name of the default application
    *
    * @return The name of the default application
    *
    */
   
  public String getDefaultApplicationClassName ()
  {
    return defaultApplicationClassName;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: isDefaultApplicationClassName
  // ----------------------------------------------------------------------
 
  /** Test if the class name corresponds to the default application class name
    *
    * @return Boolean indicating if this is the default application class name
    *
    */
   
  public boolean isDefaultApplicationClassName (String aClassName)
  {
    return getMauiApplicationClassName (aClassName).equals (defaultApplicationClassName);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getShortName
  // ----------------------------------------------------------------------
 
  /** Return the short name for the application
    *
    * @param aFolder The folder which contains the class
    * @param aClassName The target class
    *
    * @return The short name for the class
    *
    */
   
  public String getShortName (String aFolderName, String aClassName)
  {
    return getShortName (aFolderName + "/" + aClassName);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getShortName
  // ----------------------------------------------------------------------
 
  /** Return the short name for the application
    *
    * @param aName An application name
    *
    * @return The short name for the application
    *
    */
   
  public String getShortName (String aClassName)
  {
    FolderInformation theFolder = getFolder (aClassName);
    String retVal = getMauiApplicationClassName (aClassName);
    ApplicationClassWrapper theWrapper = theFolder.getWrapper (retVal);
    int thePeriod = retVal.lastIndexOf (".");
    if (thePeriod != -1)
    {
      retVal = retVal.substring (thePeriod + 1);
    }
    retVal = theWrapper.getApplicationSuite ().getName () + "." + retVal;
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSimpleShortName
  // ----------------------------------------------------------------------
 
  /** Return the short name for the class
    *
    * @param aFolder The folder which contains the class
    *
    * @param aName An application name
    *
    * @return The short name for the application without the suite name attached
    *
    */
   
  public String getSimpleShortName (String aFolder, String aClassName)
  {
    return getSimpleShortName (aFolder + "/" + aClassName);
 
 
 
  /** Return the short name for the application without the suite name attached.
    *
    * @param aName An application name
    *
    * @return The short name for the application without the suite name attached
    *
    */
   
  public String getSimpleShortName (String aClassName)
  {
    FolderInformation theFolder = getFolder (aClassName);
    String retVal = getMauiApplicationClassName (aClassName);
    ApplicationClassWrapper theWrapper = theFolder.getWrapper (retVal);
    int thePeriod = retVal.lastIndexOf (".");
    if (thePeriod != -1)
    {
      retVal = retVal.substring (thePeriod + 1);
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getApplicationClassWrapper
  // ----------------------------------------------------------------------
 
  /** Get the ApplicationClassWrapper object
    *
    * @param aApplicationName The name of the application
    *
    * @return The ApplicationClassWrapper, or null if not found
    *
    */
   
  private ApplicationClassWrapper getApplicationClassWrapper (String aApplicationName)
  {
    ApplicationClassWrapper retVal = null;
    String theApplicationName = new String (aApplicationName);
    FolderInformation theFolderInformation = getFolder (theApplicationName);
    if (theFolderInformation != null)
    {
      String theFolderName = theFolderInformation.getFolderName ();
      if (theFolderName.length () > 0 &&
        theApplicationName.startsWith (theFolderName))
      {
        theApplicationName = theApplicationName.substring (theFolderName.length () - 1);
        if (theApplicationName.startsWith ("/"))
        {
          theApplicationName = theApplicationName.substring (1);
        }
      }
      String theClassName = theFolderInformation.getClassName (theApplicationName);
      retVal = (ApplicationClassWrapper) theFolderInformation.applications.get (theClassName);
    }
    return retVal;
  }
 
 
  /** Get the ApplicationClassWrapper object
    *
    * @param aFolderName The name of the folder containing the application (or null for the default)
    * @param aApplicationName The name of the application
    *
    * @return The ApplicationClassWrapper, or null if not found
    *
    */
   
  private ApplicationClassWrapper getApplicationClassWrapper (String aFolderName, String aApplicationName)
  {
    ApplicationClassWrapper retVal = null;
    FolderInformation theFolderInformation = getFolder (aFolderName);
    if (theFolderInformation != null)
    {
      String theClassName = theFolderInformation.getClassName (aApplicationName);
      retVal = (ApplicationClassWrapper) theFolderInformation.applications.get (theClassName);
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getApplicationProperties
  // ----------------------------------------------------------------------
 
  /** Get all of the properties for an application (without the application having to be loaded)
    *
    * @param aFolderName The folder which contains the application (or null to use the default)
    * @param aFullApplicationName The fully qualified application name
    *
    * @return A Properties object containing the properties for the application, or null if not found
    *
    */
   
  public Properties getApplicationProperties (String aFolderName, String aFullApplicationName)
  {
    ApplicationClassWrapper theWrapper = getApplicationClassWrapper (aFolderName, aFullApplicationName);
    return (theWrapper == null ? null : (Properties) theWrapper.getProperties ().clone ());
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSiteInitializer
  // ----------------------------------------------------------------------
 
  /** Get a site initializer
    *
    * @param aName The name of the site initializer
    *
    * @return An I_SiteInitializer object
    *
    * @exception IllegalAccessException, InstantiationException
    *
    */
   
  public I_SiteInitializer getSiteInitializer (String aName)
    throws IllegalAccessException,
         InstantiationException
  {
    I_SiteInitializer retVal = null;
    Class theClass = (Class) initializers.get (aName);
    if (theClass != null)
    {
      retVal = (I_SiteInitializer) theClass.newInstance ();
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getApplicationFolders
  // ----------------------------------------------------------------------
 
  /** Get the application folders
    *
    * @return The application folders
    *
    */
   
  public String [] getApplicationFolders ()
  {
    String [] retVal = new String [folders.size ()];
    Iterator theFolders = folders.keySet ().iterator ();
    int i = 0;
    while (theFolders.hasNext ())
    {
      retVal [i++] = (String) theFolders.next ();
    }
    return retVal;
 
 
 
  // ----------------------------------------------------------------------
  // METHOD: getApplicationSuiteNames
  // ----------------------------------------------------------------------
 
  /** Get the application suite names
    *
    * @param aFolder The folder to look for the application suites
    *
    * @return A String array of the application suite names
    *
    */
   
  public String [] getApplicationSuiteNames (String aFolder)
  {
    FolderInformation theFolderInformation = (FolderInformation) folders.get ((aFolder == null ? "" : aFolder));
    return (theFolderInformation == null ? new String [0] : theFolderInformation.getApplicationSuiteNames ());
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getApplicationNames
  // ----------------------------------------------------------------------
 
  /** Get the application names
    *
    * @param  aFolderName            The name of the folder containing the
    *                                application suite.
    *
    * @param  aApplicationSuiteName  The name of the application suite to
    *                                use as a filter. If null, then all
    *                                Maui Application names will be
    *                                returned.
    *
    * @return  A String array of application names.
    *
    */
   
  public String [] getApplicationNames (String aFolderName, String aApplicationSuiteName)
  {
    String [] retVal = null;
   
    FolderInformation theFolder = (FolderInformation) folders.get (aFolderName);
   
    if (aApplicationSuiteName == null)
    {
      retVal = theFolder.getAllApplicationNames ();
    }
    else
    {
      ApplicationSuite theSuite = theFolder.getSuite (aApplicationSuiteName);
      retVal = theSuite.getApplicationNames ();
      int theLastIndex = 0;
      for (int i = 0; i < retVal.length; i++)
      {
        Properties theProperties = getApplicationProperties (aFolderName, retVal [i]);
        String theVisibility = theProperties.getProperty (MauiApplication.MAUI_APPLICATION_INVISIBLE);
        if (theVisibility == null || theVisibility.equalsIgnoreCase ("false"))
        {
          if (theLastIndex++ < i)
          {
            retVal [theLastIndex - 1] = retVal [i];
          }
        }
      }
     
      if (theLastIndex < retVal.length)
      {
        String [] theNewApps = new String [theLastIndex];
        System.arraycopy (retVal, 0, theNewApps, 0, theLastIndex);
        retVal = theNewApps;
      }
    }
    return retVal;
  }
 
 
 
  //++ 181 MW 2001.08.09
  // ----------------------------------------------------------------------
  // METHOD: getAllApplicationNames
  // ----------------------------------------------------------------------
 
  /** Get all the application names
    *
    * @return  A Vector of all the application names.
    *
    */
   
  public Hashtable getAllApplicationNames ()
  {
    return lowerCase;
  }
  //-- 181
 
 
 
  // ----------------------------------------------------------------------
  // METHOD: getMauiApplicationClassName
  // ----------------------------------------------------------------------
 
  /** Get the class name for a MauiApplication
    *
    * @param  aFolderName  The folder which contains the application
    *
    * @param  aName        The class name
    *
    */
   
  public String getMauiApplicationClassName (String aFolder, String aName)
  {
    String retVal = null;
   
    if (aName == null ||
      aName.trim ().length () == 0)
    {
      retVal = defaultApplicationClassName;
    }
    else
    {
      FolderInformation theFolder = getFolder (aFolder);
      retVal = (String) theFolder.getApplicationName (aName);
      if (retVal == null)
      {
        retVal = theFolder.getClassName (aName);
      }
    }
    return retVal;
  }
 
 
  /** Get the class name for a MauiApplication.
    *
    * @param  aName  The name of the MauiApplication
    *
    * @return  The class name
    *
    */
   
  public String getMauiApplicationClassName (String aName)
  {
    int theSlash = aName.lastIndexOf ("/");
    String theFolder = (theSlash == -1 ? "" : aName.substring (0, theSlash));
    String theName = (theSlash == -1 ? aName : aName.substring (theSlash + 1));
    return getMauiApplicationClassName (theFolder, theName);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getMauiApplicationAddress
  // ----------------------------------------------------------------------
 
  /** Get the application address (this includes the folder reference
    *
    * @param  aName  The name of the application
    *
    * @return  The full address, including the folder and class name
    *
    */
   
  public String getMauiApplicationAddress(String aName)
  {
    String retVal = null;
    if (aName == null ||
      aName.trim ().length () == 0)
    {
      retVal = (defaultFolder.length () == 0 ? "" : defaultFolder + "/") + defaultApplicationClassName;
    }
    else
    {
      int theQuestion = aName.indexOf ("?");
      String theName = (theQuestion == -1 ? aName : aName.substring (0, theQuestion));
      String theFolder = getFolder (theName).getFolderName ();
      retVal = (theFolder.length () == 0 ? "" : theFolder + "/") + getMauiApplicationClassName (theName);
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessions
  // ----------------------------------------------------------------------
 
  /** Get all of the sessions running a particular application
    *
    * @param  aApplicationName  The name of the application to check
    *
    */
   
  public HTTPSession[] getSessions(String anApplicationName)
  {
    HTTPSession [] retVal = null;
    SessionInformation theSessionInformation = (SessionInformation)sessions.get(getMauiApplicationClassName(anApplicationName));
   
    if (theSessionInformation != null)
    {
      retVal = theSessionInformation.toArray();
    }
   
    return (retVal == null ? new HTTPSession [0] : retVal);
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessionCount
  // ----------------------------------------------------------------------
 
  /** Get all of the sessions running a particular application
    *
    * @param  aApplication  The application to check
    *
    */
   
  public HTTPSession[] getSessions(MauiApplication aApplication)
  {
    return getSessions(aApplication.getApplicationName());
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessionCount
  // ----------------------------------------------------------------------
 
  /** Get a count of the number of sessions currently running an application
    *
    * @param  aApplication  A Maui application.
    *
    * @return               The number of sessions currently running.
    *
    */
   
  public int getSessionCount(MauiApplication aApplication)
  {
    return getSessions(aApplication).length;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessionCount
  // ----------------------------------------------------------------------
 
  /** Get a count of all of the sessions currently running
    *
    * @return Count of all of the sessions currently running
    *
    */
   
  public int getSessionCount()
  {
    return HTTPSession.getSessionCount();
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessionApplications
  // ----------------------------------------------------------------------
 
  /** Get all of the applications for an HTTPSession, or for all applications
    *
    * @param  aSession  The session to get applications for
    *
    */
   
  public MauiApplication[] getSessionApplications(HTTPSession aSession)
  {
    return (aSession == null ? getRunningApplications() : aSession.getCachedMauiApplications());
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getSessionApplications
  // ----------------------------------------------------------------------
 
  /** Get all of the applications for an HTTPSession id, or for all applications
    *
    * @param  aSessionID  The session id
    *
    */
   
  public MauiApplication[] getSessionApplications(String aSessionID)
  {
    MauiApplication [] retVal;
   
    if (aSessionID == null)
    {
      retVal = getRunningApplications ();
    }
    else
    {
      HTTPSession theSession = HTTPSession.getSession (aSessionID);
      retVal = getSessionApplications (theSession);
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: getRunningApplications
  // ----------------------------------------------------------------------
 
  /** Get the list of all running applications
    *
    * @return  An array of all of the running applications
    *
    */
   
  public MauiApplication[] getRunningApplications ()
  {
    Enumeration theSessionInformations = sessions.elements ();
    MauiApplication [] retVal = new MauiApplication [sessions.size ()];
    int i = 0;
    while (theSessionInformations.hasMoreElements ())
    {
      retVal [i++] = ((SessionInformation) theSessionInformations.nextElement ()).getMauiApplication ();
    }
    return retVal;
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: sessionCreated
  // ----------------------------------------------------------------------
 
  /** Notification of session creation
    *
    * @param  aSessionEvent  The event object describing the session
    *
    */
   
  public void sessionCreated (SessionEvent aSessionEvent)
  {
    System.out.println ("Session created: " + ((HTTPSession) aSessionEvent.getSource ()).getSessionID ());
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: sessionDeleted
  // ----------------------------------------------------------------------
 
  /** Notification of session deletion.
    *
    * @param  aSessionEvent  The event object describing the session
    *
    */
   
  public void sessionDeleted (SessionEvent aSessionEvent)
  {
    HTTPSession theSession = (HTTPSession) aSessionEvent.getSource ();
    System.out.println ("Session removed: " + theSession.getSessionID ());
   
    //
    //  Purge this session from the sessions to applications cross-reference table
    MauiApplication [] theApplications = theSession.getCachedMauiApplications ();
    for (int i = 0; i < theApplications.length; i++)
    {
      String theName = getMauiApplicationClassName (theApplications [i].getApplicationName ());
      SessionInformation theSessionInformation = (SessionInformation) sessions.get (theName);
      if (theSessionInformation != null)
      {
        theSessionInformation.removeSession (theSession);
        if (theSessionInformation.isEmpty ())
        {
          sessions.remove (theName);
        }
      }
    }
  }
 
 
  // ----------------------------------------------------------------------
  // METHOD: applicationAdded
  // ----------------------------------------------------------------------
 
  /** Notification of the addition of an application.
    *
    * @param  aSessionEvent  The event object describing the session
    *
    */
 
  public void applicationAdded (SessionEvent aSessionEvent)
  {
    String theApplicationName = getMauiApplicationClassName (aSessionEvent.getMauiApplication ().getApplicationName ());
    System.out.println ("Application " + theApplicationName +
              " added to session: " + ((HTTPSession) aSessionEvent.getSource ()).getSessionID ());
             
    SessionInformation theSessionInformation = (SessionInformation) sessions.get (theApplicationName);
    if (theSessionInformation == null)
    {
      theSessionInformation = new SessionInformation (aSessionEvent.getMauiApplication ());
      sessions.put (theApplicationName, theSessionInformation);
    }
    theSessionInformation.addSession ((HTTPSession) aSessionEvent.getSource ());
  }
 
  /** Notification of the addition of an application.
    *
    * @param  aSessionEvent  The event object describing the session
    *
    */
 
  public void applicationRemoved (SessionEvent aSessionEvent)
  {
    HTTPSession theSession = (HTTPSession) aSessionEvent.getSource ();
    String theApplicationName = getMauiApplicationClassName (aSessionEvent.getMauiApplication ().getApplicationName ());
    System.out.println ("Application " + theApplicationName +
              " removed from session: " + theSession.getSessionID ());
             
    SessionInformation theSessionInformation = (SessionInformation) sessions.get (theApplicationName);
    if (theSessionInformation != null)
    {
      theSessionInformation.removeSession (theSession);
      if (theSessionInformation.isEmpty ())
      {
        sessions.remove (theApplicationName);
      }
    }
  }
 
  public void run ()
  {
    Thread.currentThread ().setName ("AppMgr - AutoLoad");
    do
    {
      if (extensionLocation != null)
      {
        scanForExtensions ();
      }
     
      if (applicationLocation != null)
      {
        buildApplicationSuites (new File (applicationLocation));
      }
     
      if (firstTime)
      {
        siteInitialization (scm.getProperties ());
        //synchronized (this)
        //{
          firstTime = false;
        //  notify ();
        //}
      }
     
      if (autoReload)
      {
        try
        {
          Thread.sleep (applicationScanTime);
        }
        catch (InterruptedException e)
        {
        }
      }
    } while (autoReload);
  }
}


// ========================================================================
// INTERFACE: I_JarCallback
// ========================================================================

interface I_JarCallback
{
  public void mauiAppFound (Class aMauiAppClass);
  public void initializerFound (Class aInitializer);
}


// ========================================================================
// CLASS: MauiClassLoader
// ========================================================================

/** This is the MauiClassLoader object.  It is a simple ClassLoader which is used to load classes from the jar
  * files contained with the Applications folder hierarchy.
  *
  */
 
class MauiClassLoader extends ClassLoader
{
 
 
  private Hashtable classEntries = new Hashtable (30);
  private Hashtable resourceEntries = new Hashtable (10);
  private Hashtable resourceJars = new Hashtable (10);
  private static final Class mauiApplicationClass = MauiApplication.class;
  protected boolean loadAll = false;
  protected boolean loadAllProperties = false;
  protected Vector newClasses = new Vector (100);
  private boolean applicationsLoader;
  private MauiClassLoader parent;


  // --------------------------------------------------------------------
  // CONSTRUCTORS
  // --------------------------------------------------------------------

  protected MauiClassLoader (ClassLoader aParent)
  {
    //super (aParent);
    super ();
    parent = (MauiClassLoader) aParent;
    applicationsLoader = true;
    initialize ();
  }
 
 
  protected MauiClassLoader ()
  {
    super ();
    applicationsLoader = false;
    initialize ();
  }
 
  private void initialize ()
  {
    ServerConfigurationManager theSCM = ServerConfigurationManager.getInstance ();
    loadAll = theSCM.getProperty (theSCM.MAUI_AUTO_RELOAD).equals ("true");
  }
 
  private byte [] readZipEntry (InputStream aInput, int aSize)
    throws IOException
  {
    final int theTotalLength = aSize;
    byte [] retVal = new byte [theTotalLength];
    int theBufferOffset = 0;
    while (theBufferOffset < theTotalLength)
    {
      int theBytesRead = aInput.read (retVal, theBufferOffset, theTotalLength - theBufferOffset);
      theBufferOffset += theBytesRead;
    }
    return retVal;
  }
 
  protected void setLoadAllProperties (boolean aLoadAllProperties)
  {
    loadAllProperties = aLoadAllProperties;
  }

  // --------------------------------------------------------------------
  // METHOD: findObject
  // --------------------------------------------------------------------

  /** Find an object within the jar file.  This can be a class or
    * resource.  For each access to the jar file, an entry is
    * added to a "caching" Hashtable, for faster subsequent accesses.
    *
    * @param  aUseName     The name to use for throwing exceptions (ie.
    *                      the actual class or resource name, rather than
    *                      the key of access).
    *
    * @param  aTable       The Hashtable to use for retrieving cached
    *                      objects.
    *
    * @param  aNeedClass   Boolean indicating if this is a request for a
    *                      class or resource.
    *
    * @return              The retrieved Object (Class or Resource).
    *
    * @exception  ClassNotFoundException
    *                      Thrown if the Class isn't found.
    *
    */
   
  protected Object findObject (String aUseName,
                 Hashtable aTable,
                 boolean aNeedClass)
    throws ClassNotFoundException
  {
    Object retVal = aTable.get (aUseName);
   
    if (retVal != null && retVal instanceof byte [])
    {
      if (aNeedClass)
      {
        retVal = defineClass (aUseName, (byte []) retVal, 0, ((byte []) retVal).length);
        aTable.put (aUseName, retVal);
      }
      else
      {
        retVal = new ByteArrayInputStream ((byte []) retVal);
      }
    }
    return retVal;
  }
  /*  if (retVal instanceof ZipFile)
    {
      String theSearchName = new String (aUseName);
      if (aNeedClass)
      {
        theSearchName = theSearchName.replace ('.', '/') + ".class";
      }
      ZipEntry theEntry = ((ZipFile) retVal).getEntry (theSearchName);
      if (theEntry == null)
      {
        throw new ClassNotFoundException (aUseName);
      }
     

      InputStream theInput = null;
      try
      {
        theInput = ((ZipFile) retVal).getInputStream (theEntry);
        if (aNeedClass)
        {
          //
          //  This is a request for a class load
          //
          //definePackage (theEntry);
          int theSize = (int) theEntry.getSize ();
          byte [] theClassDefinition = readZipEntry (theInput, theSize);
          retVal = defineClass (aUseName, theClassDefinition, 0, theSize);
          aTable.put (aUseName, retVal);  // Replace the entry with the class definition
        }
        else
        {
          //
          //  Trying for resource loading
          //
          //  Return an InputStream for the resource, which can be wrapped up as an URL later
          //
          if (loadAll)
          {
            byte [] theResource = readZipEntry (theInput, (int) theEntry.getSize ());
            aTable.put (aUseName, theResource);
            retVal = new ByteArrayInputStream (theResource);
          }
          else
          {
            retVal = theInput;
          }
        }
      }
      catch (IOException e)
      {
        throw new ClassNotFoundException (aUseName);
      }
      finally
      {
        if (theInput != null)
        {
          try
          {
            theInput.close ();
          }
          catch (IOException e1)
          {
          }
        }
      }
    }
    else if (!aNeedClass)
    {
      if (retVal instanceof byte [])
      {
        retVal = new ByteArrayInputStream ((byte []) retVal);
      }
    }
    return retVal;
  }*/
 
 
  // --------------------------------------------------------------------
  // METHOD: loadClass
  // --------------------------------------------------------------------
 
  /** This extends the ClassLoader's loadClass method.  It is for
    * debugging purposes only. If the logging threshold is at the debug
    * level, then this will add a message to the log indicating what
    * class is being loaded.
    *
    * @param  aName     The name of the Class to load.
    *
    * @param  aResolve  Boolean indicating if the Class should be
    *                   resolved or not.
    *
    */
   
  protected Class loadClass (String aName, boolean aResolve)
    throws ClassNotFoundException
  {
    Class retVal = null;
    System.out.println (new DebugString("Loading class " + aName));
    if (!aName.equals ("com.bitmovers.maui.MauiRuntimeEngine"))
    {
      try
      {
        retVal = (Class) findObject (aName,
                       classEntries,
                       true);
      }
      catch (ClassNotFoundException e)
      {
      }

      if (retVal == null && applicationsLoader)
      {
        try
        {
          retVal = parent.loadClass (aName, aResolve);
          classEntries.put (aName, retVal);
        }
        catch (ClassNotFoundException e)
        {
        }
       
      }
    } 
       
    if (retVal == null)
    {
      retVal = findSystemClass (aName);
      classEntries.put (aName, retVal);
    }

    if (retVal != null && aResolve)
    {
      resolveClass (retVal);
    }
    return retVal;
  }
 
 
  // --------------------------------------------------------------------
  // METHOD: findClass
  // --------------------------------------------------------------------
 
  /** If the default ClassLoader can't find the Class, then this
    * ClassLoader will try to find it.
    *
    * @param      aName  The name of the Class to load.
    *
    * @return            The loaded Class
    *
    * @exception  ClassNotFoundException 
    *                    Thrown if the Class isn't found.
    *
    */
   
  /*protected Class findClass (String aName)
    throws ClassNotFoundException
  {
    return (Class) findObject (aName.replace ('.', '/') + ".class",
                   aName,
                   classEntries,
                   true);
  }*/
 
 
  // --------------------------------------------------------------------
  // METHOD: findResourceStream
  // --------------------------------------------------------------------
 
  /** Find a resource, and return an input stream to it
    *
    * @param   aName        The name of the resource.
    *
    * @return               An InputStream to the resource.
    *
    * @throws  IOException  If the resource isn't found, or something
    *                       else goes wrong.
    *
    */
   
  protected InputStream findResourceStream (String aName)
    throws IOException
  {
    InputStream retVal = null;
    try
    {
      StringBuffer theResourceName = new StringBuffer (aName);
      int theNameLength = theResourceName.length ();
      boolean theSuffix = aName.endsWith (".properties");
      if (theSuffix)
      {
        theNameLength -= 11;
      }
      else
      {
        theResourceName.append (".properties");
      }
     
      for (int i = 0; i < theNameLength; i++)
      {
        if (theResourceName.charAt (i) == '.')
        {
          theResourceName.setCharAt (i, '/');
        }
      }
      String theName = theResourceName.toString ();
      retVal = (InputStream) findObject (theName, resourceEntries, false);
    }
    catch (ClassNotFoundException e)
    {
    }
    return retVal;
  }
 
 
  // --------------------------------------------------------------------
  // METHOD: findResourceStream
  // --------------------------------------------------------------------
 
  /** WARNING: NOT IMPLEMENTED YET <p>
    *
    * Find a resource, and return a URL to it
    *
    * @param aName The name of resource
    *
    * @return The URL for the resource
    *
    * @invisible
    *
    */
   
  /*protected URL findResource (String aName)
  {
    URL retVal = null;
    if (resourceEntries.get (aName) != null)
    {
      String theJarName = (String) resourceJars.get (aName);
      try
      {
        retVal = new URL ("jar:file:" + theJarName + "!/" + aName);
      }
      catch (IOException e)
      {
      }
    }
    return retVal;
  }*/
 
  public InputStream getResourceAsStream (String aName)
  {
    InputStream retVal = null;
   
    byte [] theResourceArray = (byte []) resourceEntries.get (aName);
    if (theResourceArray == null)
    {
      if (parent != null)
      {
        retVal = parent.getResourceAsStream (aName);
      }
    }
    else
    {
      retVal = new ByteArrayInputStream (theResourceArray);
    }
    return retVal;
  }
 
 
  // --------------------------------------------------------------------
  // METHOD: findResources
  // --------------------------------------------------------------------
 
  /** WARNING: NOT IMPLEMENTED YET <p>
    * Find the resources which match the name
    *
    */
   
  protected Enumeration findResources (String aName)
    throws IOException
  {
    return null;
  }
 
 
  // --------------------------------------------------------------------
  // METHOD: scanJarFile
  // --------------------------------------------------------------------
 
  /** Scan a jar file for Classes and Resources, and catalogue them.
    *
    * @param aJarFile The Jar file to scan
    * @param aCallback A callback object which is used for cataloguing a MauiApplication
    * @param aLoad Boolean indicating if this is loading or unloading
    *
    * @exception IOException If the jar file scan fails
    *
    */
   
  protected void scanJarFile (final ZipFile aJarFile,
                final I_JarCallback aCallback)
    throws IOException
  {
    //
    //  Create two lists of entries one for class files, and one for resources (eg. everything else)
    //  For now, ignore the manifest
    //
    ZipEntry theEntry;
    String theEntryName = null;
    Enumeration theEntries = aJarFile.entries ();
    while (theEntries.hasMoreElements ())
    {
      theEntry = (ZipEntry) theEntries.nextElement ();
      if (!theEntry.isDirectory ())
      {
        theEntryName = theEntry.getName ();
        InputStream theInput = aJarFile.getInputStream (theEntry);
        byte [] theData = readZipEntry (theInput, (int) theEntry.getSize ());
        if (theEntryName.endsWith (".class"))
        {
          theEntryName = theEntryName.substring (0, theEntryName.length () - 6).replace ('/', '.');
          classEntries.put (theEntryName, theData);
          newClasses.addElement (theEntryName);
        }
        else if (loadAllProperties || theEntryName.toLowerCase ().endsWith (".properties"))
        {
          resourceEntries.put (theEntryName, theData);
        }
        theInput.close ();
       
         
          /*if (loadAll)
          {
            try
            {
              InputStream theInputStream = (InputStream) findObject (theEntryName,
                                           resourceEntries,
                                           false);
              try
              {
                theInputStream.close ();
              }
              catch (IOException e)
              {
              }
            }
            catch (Exception e)
            {
            }
          }
          else
          {
            resourceEntries.remove (theEntryName);
          }
        }*/
       
      }
    }
   
    if (applicationsLoader)
    {
      if (aCallback != null)
      {
        loadAllEntries (aCallback);
      }
      else
      {
        newClasses.removeAllElements ();
      }
    }
  }
 
  protected void loadAllEntries (I_JarCallback aCallback)
  {
    String theEntryName;
    Enumeration theClassEntries = newClasses.elements ();
    Class theInitializer = I_SiteInitializer.class;
    while (theClassEntries.hasMoreElements ())
    {
      theEntryName = (String) theClassEntries.nextElement ();
      try
      {
        Class theClass = Class.forName (theEntryName, true, this);
        if (aCallback != null)
        {
          if (mauiApplicationClass.isAssignableFrom (theClass) &&
            !Modifier.isAbstract (theClass.getModifiers ()) &&
            !theClass.equals (mauiApplicationClass))
          {
            aCallback.mauiAppFound (theClass);
          }
          else if (theInitializer.isAssignableFrom (theClass))
          {
            aCallback.initializerFound (theClass);
          }
        }
      }
      catch (ClassNotFoundException e)
      {
        System.err.println ("[MauiClassLoader] unexpected exception on " + theEntryName + " : " + e);
        e.printStackTrace ();
      }
      catch (NoClassDefFoundError e)
      {
        System.err.println ("[MauiClassLoader] unexpected exception on " + theEntryName + " : " + e);
        e.printStackTrace ();
      }
    }
    newClasses.removeAllElements ();
  }
 
 
  // --------------------------------------------------------------------
  // METHOD: addJarFile
  // --------------------------------------------------------------------
 
  /** Added a jar file to the Maui applications database
    *
    * @param aJarFile The jar file to add
    *
    * @param aCallback The callback object which is used for cataloguing a MauiApplication
    *
    * @exception IOException If the jar file read fails
    *
    */
 
  protected void addJarFile (final File aJarFile,
                 final I_JarCallback aCallback)
    throws IOException
  {
    ZipFile theZipFile = new ZipFile (aJarFile);
    //
    //  Now scan through, and preload the ZipEntry objects for faster class loading
    //
    scanJarFile (theZipFile, aCallback);
    theZipFile.close ();
      /*if (theIndex == currentCapacity)
      {
        currentCapacity++;
      }
    }
    catch (Exception e)
    {
      if (e instanceof IOException)
      {
        throw (IOException) e;
      }
      e.printStackTrace ();
    }
    finally
    {
      if (loadAll && applicationsLoader && jarFiles [theIndex] != null)
      {
        try
        {
          jarFiles [theIndex].close ();
          new FileInputStream (aJarFile).close ();
        }
        catch (IOException e)
        {
        }
        jarFiles [theIndex] = null;
      }
    }*/
  }
 
  protected void loadAllExtensions ()
  {
    loadAllEntries (null);
    /*for (int i = 0; i < currentCapacity; i++)
    {
      if (jarFiles [i] != null)
      {
        try
        {
          jarFiles [i].close ();
        }
        catch (IOException e)
        {
        }
        jarFiles [i] = null;
      }
    }
    currentCapacity = 0;*/
  }
}


// ========================================================================
//                                               (c) 2001 Bitmovers Systems
TOP

Related Classes of com.bitmovers.maui.engine.ApplicationManager

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.