Listing of Source cgi/SmartApplet.java
package se.entra.phantom.server.http;

import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NamedNodeMap;
import com.netphantom.server.utils.PublishApplication;
import com.netphantom.server.utils.PublishApplication.Application;
import se.entra.phantom.client.Phantom;
import se.entra.phantom.server.ClientSessionManager;
import se.entra.phantom.server.EventID;
import se.entra.phantom.server.EventManager;
import se.entra.phantom.server.IReloadable;
import se.entra.phantom.server.IniFile;
import se.entra.phantom.server.PhantomRuntime;
import se.entra.phantom.server.WindowsLikeFilenameFilter;

/**
 * The HTML inline CGI is used to choose the correct HTML Applet tag or Java Plug-in.
 * It used the Browser identification (UserAgent string) to detect the settings.
 * See "SmartApplet.ini" for more information.
 */
public class SmartApplet implements IncludeCgiHtmlResourceInterface
{
  /**
   * The INI file.
   */
  private static IniFile ini;

  /**
   * The file name.
   */
  private static final String iniName = "SmartApplet.ini";
  
  ///

  /**
   * The Web Server calls this routine to initialize or reinitialize
   * the "SmartApplet.ini" file.
   *
   * @throws  IOException  for IO errors.
   */
  public static void loadSettings() throws IOException
    {
    try
      {
      ini=new IniFile(iniName);
      }
    catch(IOException e)
      {
      throw new IOException("SmartApplet: Failure reading \""+iniName+"\": "+e);
      }
    }
  
  ///

  /**
   * This method should return an <code>HtmlResource</code> instance with the document
   * that should be sent to the client.
   *
   * <p>The current directory of the CGI for the client agent is defined in the
   * <code>HttpSession</code> class instance as the resource requested without
   * the "/name.cgi" text.
   *
   * @throws  IOException  for IO errors.
   */
  @Override
  public HtmlResource performAction(HttpSession session,HttpResource httpResource,Element element) throws IOException
    {
    // Check if no INI file.
    if ( ini==null )
      throw new IOException("SmartApplet: \""+iniName+"\" is not loaded");
    
    // Check if it can be reloaded.
    if ( ini.needsReload() )
      if ( ini.reload()==IReloadable.FAILURE )
        EventManager.logEvent("SmartApplet: Reloading of INI file \""+iniName+"\" failed",EventID.EVENTCLASS_WARNING);

    // Section name to use.
    String section=element.getAttribute("section");
    if ( section==null || section.length() <= 0)
      section="BrowserSelection";

    // File name to use.
    String fileName=null;

    // Check the user agent for the matching string.
    String userAgent=session.environment.userAgent;
    if ( userAgent!=null )
      {
      // Enumerate all "nn.id" items.
      Enumeration<String> e=ini.getItems(section);
      if ( e==null )
        throw new IOException("SmartApplet: \""+iniName+"\" does not contain is not ["+section+"] section");

      while ( e.hasMoreElements() && fileName==null )
        {
        String id=e.nextElement().toUpperCase();
        if ( id.endsWith(".ID") )
          {
          String s=ini.getData(section,id);
          if ( s!=null )
            {
            StringTokenizer t=new StringTokenizer(s,",",false);
            for ( ;; )
              {
              // Set found!
              if ( !t.hasMoreTokens() )
                {
                fileName=ini.getData(section,id.substring(0,id.length()-2)+"file");
                break;
                }

              // Check for match or mismatch (string starts with '!').
              String check=t.nextToken();
              boolean doMatch=(check.length()==0 || check.charAt(0)!='!');
              if ( !doMatch )
                check=check.substring(1);

              // Check for browser match.
              if ( WindowsLikeFilenameFilter.isMatching(userAgent,check)!=doMatch )
                break;
              }
            }
          }
        }
      }

    // Check if a file name is found. If no, use the "sorry" file name.
    if ( fileName==null )
      {
      fileName=ini.getData(section,"sorry");
      if ( fileName==null )
        throw new IOException("SmartApplet: no 'sorry' item in the ["+section+"] section in '"+iniName+"'");
      }

    // Load the HTML resource.
    HtmlResource res=session.webServer.loadAbsoluteDocument(fileName);

    // Now set all the parameters (except CGI).
    HashMap<String,String> vars=new HashMap<String,String>();
    NamedNodeMap nl=element.getAttributes();
    String appIDs="";
    if ( nl!=null )
      {
      int numAttributes=nl.getLength();
      for ( int ii=0; ii<numAttributes; ++ii )
        {
        // Skip CGI and SECTION parameters.
        Node attr=nl.item(ii);
        String s=attr.getNodeName();
        if ( s==null || s.equalsIgnoreCase("CGI") || s.equalsIgnoreCase("SECTION") )
          continue;
        
        s=s.toUpperCase();
        String d=attr.getNodeValue();
        if ( d==null )
          d="";
        
        String v=HtmlVariable.getTextData(session,res,d);
        vars.put(s,v);
        
        // Save the APP parameter, but remove "Search from first".
        if ( s.equals("APP") )
          {
          appIDs=v.toUpperCase();
          if ( appIDs.startsWith("!") )
            appIDs=appIDs.substring(1);
          }
        }
      }
    
    // Check for *APP: replace with the application IDs in order to get
    // the correct JAR files.
    if ( appIDs.startsWith("*") )
      {
      Application a=PublishApplication.getApplication(appIDs.substring(1));
      if ( a==null )
        throw new IOException("SmartApplet: The application ID "+appIDs+" is not defined!");
      
      appIDs=a.getRuntimeAppIDs();
      
      if ( a.draggable )
        {
        vars.put("DRAGGABLE","true");
        vars.put("JNLP_REF",a.jnlpFileName);
        vars.put("DRAGICON",a.dragIcon? "1": "0");
        }
      
      if ( a.verbose )
        vars.put("VERBOSE","1");

      if ( a.minimumMB>0 || a.maximumMB>0 )
        vars.put("JAVA_ARGUMENTS",((a.minimumMB>0? "-Xms"+a.minimumMB+'m': "")+(a.maximumMB>0? " -Xmx"+a.maximumMB+'m': "")).trim());
      
      // Transfer all extra parameters also, exception the ones above.
      for ( Entry<String,String> e: a.extraParams.entrySet() )
        {
        // Name and value of parameter.
        String n=e.getKey().toUpperCase();
        String v=e.getValue();

        // Set variable if not one of the special ones.
        if ( !" DRAGGABLE JNLP_REF DRAGICON VERBOSE JAVA_ARGUMENTS ".contains(' '+n+' ') )
          vars.put(n,v);
        }
      }

    // Find the extra JAR files that needs to be loaded along with NetPhantomClient.jar.
    String jars="";
    ClientSessionManager csm=ClientSessionManager.getServerAdminInterface();
    if ( appIDs.isEmpty() )
      appIDs=csm.getDefaultRuntimeApplicationName();
    
    if ( appIDs.equals("__CURRENT__") && csm.isEditor() )
      {
      PhantomRuntime rt=se.entra.phantom.server.phed.Start.runtimeData;
      if ( rt==null )
        {
        // Don't care: skip it, but log the event.
        EventManager.logEvent("SmartApplet: Runtime application ID __CURRENT__ is not found: skipping Client Jar files",EventID.EVENTCLASS_WARNING);
        }
      else
        {
        String [] as=rt.getClientJarEntries();
        for ( String s: as )
          jars+=",/*__CURRENT__/"+s;
        }
      }
    else
      {
      for ( StringTokenizer st=new StringTokenizer(appIDs,",;:",false); st.hasMoreTokens(); )
        {
        String app=st.nextToken();
        if ( app.equalsIgnoreCase("SERVERADMIN") )
          {
          // Skip for now...
          continue;
          }
        
        PhantomRuntime rt=csm.getRuntimeApplication(app);
        if ( rt==null )
          {
          // Don't care: skip it, but log the event.
          EventManager.logEvent("SmartApplet: Runtime application ID "+app+" is not found: skipping Client Jar files",EventID.EVENTCLASS_WARNING);
          continue;
          }
        
        String [] as=rt.getClientJarEntries();
        for ( String s: as )
          jars+=",/*"+app+"/"+s;
        }
      }
    
    // Set the client Jars.
    vars.put("__NP__CLIENTJARS",jars);
    
    // Make sure a variable for StartClass is set.
    s=vars.get("STARTCLASS");
    if ( s==null || s.isEmpty() )
      vars.put("STARTCLASS",Phantom.class.getName());
    
    // Make sure width and height of applet is set.
    s=vars.get("APPLETWIDTH");
    if ( s==null || s.isEmpty() )
      vars.put("APPLETWIDTH","100%");
    
    s=vars.get("APPLETHEIGHT");
    if ( s==null || s.isEmpty() )
      vars.put("APPLETHEIGHT","100%");
    
    // Extra parameter support: for all $PARAM, also set the PARAM to same value
    // if not already present.
    for ( Entry<String,String> e: vars.entrySet() )
      {
      String n=e.getKey();
      String v=e.getValue();
      session.varSet(n,v);
      if ( n.startsWith("$") && vars.get(n.substring(1))==null )
        session.varSet(n.substring(1),v);
      }
    
    // Return the HTML document.
    return res;
    }
}