// ServletWrapper.java
// $Id: ServletWrapper.java,v 1.18 1998/01/22 14:11:52 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.servlet;

import java.io.* ;
import java.util.*;

import javax.servlet.*;

import org.w3c.tools.resources.store.*;
import org.w3c.util.*;
import org.w3c.www.mime.*;
import org.w3c.www.http.*;
import org.w3c.jigsaw.http.*;
import org.w3c.tools.resources.*;

/**
 * @author Alexandre Rafalovitch <alex@access.com.au>
 * @author Anselm Baird-Smith <abaird@w3.org>
 * @author Benoit Mahe <bmahe@w3.org>
 */

public class ServletWrapper extends FramedResource implements ServletConfig
{

  private LocalServletLoader loader  = null;

  protected final static boolean debug = false;

  /**
   * Attributes index - The servlet class name.
   */
  protected static int ATTR_SERVLET_CLASS = -1 ;
  /**
   * Attribute index - The init parameters for that servlet.
   */
  protected static int ATTR_PARAMETERS = 1;
  /**
   * Attribute index - Our parent-inherited servlet context.
   */
  protected static int ATTR_SERVLET_CONTEXT = -1;

  static {
    Attribute a   = null ;
    Class     cls = null ;
    try {
      cls = Class.forName("org.w3c.jigsaw.servlet.ServletWrapper") ;
    } catch (Exception ex) {
      ex.printStackTrace();
      System.exit(0);
    }
    // The servlet class attribute.
    a = new StringAttribute("servlet-class"
			    , null
			    , Attribute.EDITABLE | Attribute.MANDATORY) ;
    ATTR_SERVLET_CLASS = AttributeRegistry.registerAttribute(cls, a) ;
    // This servlet init parameters
    a = new PropertiesAttribute("servlet-parameters"
				, null
				, Attribute.EDITABLE);
    ATTR_PARAMETERS = AttributeRegistry.registerAttribute(cls, a);
    // Our servlet context:
    a = new ObjectAttribute("servlet-context"
			    , "org.w3c.jigsaw.servlet.ServletDirectoryFrame"
			    , null
			    , Attribute.DONTSAVE);
    ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
  }
    
  /**
   * The servlet wrapped within that Jigsaw resource.
   */
  protected Servlet servlet = null;
  /**
   * Is out servler initialized ?
   */
  protected boolean inited = false;

  /**
   * The Path where we can find the servlet class file.
   */
  public File getServletDirectory() {
    ServletDirectoryFrame dir = 
      (ServletDirectoryFrame) getValue(ATTR_SERVLET_CONTEXT, null);
    if (dir == null)
      return null;
    else return dir.getDirectory();
  }

  /**
   * Servlet stub implementation - Get an init parameter value.
   */

  public synchronized String getInitParameter(String string) {
    ArrayDictionary d = getServletParameters();
    String          v = (d != null) ? (String) d.get(string) : null;
    return v;
  }

  /**
   * Servlet stub implementation - Get all init parameters.
   */

  public synchronized Enumeration getInitParameterNames() {
    // Convert init parameters to hashtable:
    ArrayDictionary d = getServletParameters();
    return (d != null) ? d.keys() : new EmptyEnumeration();
  }

  /**
   * Servlet stub implementation - Get that servlet context.
   */

  public ServletContext getServletContext() { 
    return (ServletContext) getValue(ATTR_SERVLET_CONTEXT, null);
  }

  protected void checkServletClass() {
    if (loader.classChanged(getServletClass())) {
      inited = launchServlet();
    }
  }

  protected boolean isInited() {
    return inited;
  }

  protected void service(Request request, Reply reply)
    throws ServletException, IOException
  {
    servlet.service(new JigsawHttpServletRequest(servlet, request),
		    new JigsawHttpServletResponse(request, reply));
  }

  /**
   * Get the class name of the wrapped servlet.
   * @return The class name for the servlet if attribute is defined. Otherwise
   * the class name is deduced from the resource identifier.
   */

  public String getServletClass()
  {
    String sclass =  getString(ATTR_SERVLET_CLASS, null);
    if (sclass == null) {
      String ident = getIdentifier();
      if (ident.endsWith(".class"))
	sclass = ident;
    }
    return sclass;
  }

  /**
   * Get the init parameters for our wrapped servlet.
   * @return An ArrayDictionary instance if the attribute is defined, 
   * <strong>false</strong> otherwise.
   */

  public ArrayDictionary getServletParameters() {
    return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
  }

  /**
   * Catch assignements to the servlet class name attribute.
   * <p>When a change to that attribute is detected, the servlet is
   * automatically reinitialized.
   */

  public void setValue(int idx, Object value) {
    super.setValue(idx, value);
    if ((idx == ATTR_SERVLET_CLASS) && (value != null))
      inited = launchServlet();
  }

  /**
   * Destroy the servlet we are wrapping.
   */

  protected synchronized void destroyServlet() {
    if (servlet != null) {
      servlet.destroy();
      servlet = null;
    }
  }

  /**
   * Get the servlet we are wrapping.
   * @return A servlet instance, if the servlet is alredy running, 
   * <strong>null</strong> otherwise.
   */

  public Servlet getServlet() {
    return servlet;
  }

  /**
   * Initialize our servlet from the given (loaded) class.
   * @param cls The servlet loaded main class.
   * @return A boolean, <strong>true</strong> if servlet was successfully
   * initialised, <strong>false</strong> otherwise.
   */

  protected boolean launchServlet(Class cls) {
    try {
      servlet = (Servlet) cls.newInstance();
      servlet.init((ServletConfig) this);
    } catch (Exception ex) {
      String msg = ("unable to initialize servlet, exception: "+
		    ex.getMessage());
      if ( debug )
	ex.printStackTrace();
      getServer().errlog(this, msg);
      return false;
    }
    return (servlet != null) ;
  }

  /**
   * Launch the servlet we are wrapping.
   * <p>This method either succeed, or the wrapper resource itself will fail
   * to initialize, acting as transparently as possible (in some sense).
   * @return A boolean, <strong>true</strong> if servlet launched.
   */

  protected boolean launchServlet() {
    // Get and check the servlet class:
    if ( servlet != null )
      destroyServlet();
    String clsname = getServletClass();
    if ( clsname == null ) {
      getServer().errlog(this, "no servlet class attribute defined.");
      return false;
    } else {
      Class c = null;
      try {
	if (loader.classChanged(clsname))
	  loader = new LocalServletLoader(this.loader);
	c = loader.loadServletClass(clsname, true);
      } catch (ClassNotFoundException ex) {
	if ( debug )
	  ex.printStackTrace();
	String msg = ("unable to load servlet class \""+
		      getServletClass()+
		      "\" : "+
		      ex.getMessage());
	getServer().errlog(msg);
      }
      return (c != null) ? launchServlet(c) : false;
    }
  }

  public void notifyUnload() {
    destroyServlet();
  }

  /**
   * Initialize this servlet wrapper resource.
   * After the wrapper itself is inited, it performs the servlet 
   * initialzation.
   * @param values The default attribute values.
   */

  public void initialize(Object values[]) {
    super.initialize(values);
    loader = new LocalServletLoader(this);
    // Initialize the wrapped servlet (is now the rigt time ?)
    if ( getServletClass() != null )
      inited = launchServlet();
    try {
      Class wrapper_frame = 
	Class.forName("org.w3c.jigsaw.servlet.ServletWrapperFrame");
      ResourceFrame frame = getFrame(wrapper_frame);
      if (frame == null) {
	Hashtable defs = new Hashtable(3);
	defs.put("identifier", "servlet-wrapper-frame");
	registerFrame( new ServletWrapperFrame(), defs);
      }
    } catch (ClassNotFoundException ex) {
      throw new RuntimeException("can't register ServletWrapperFrame : "+
				 ex.getMessage());
    }
  }

}








