Package org.w3c.jigsaw.servlet

Source Code of org.w3c.jigsaw.servlet.ServletWrapper$ServletRunner

// ServletWrapper.java
// $Id: ServletWrapper.java,v 1.78 2007/02/11 10:50:01 ylafon 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.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;

import java.util.Enumeration;
import java.util.ArrayList;
import java.util.NoSuchElementException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.UnavailableException;

import org.w3c.tools.timers.EventHandler;
import org.w3c.tools.timers.EventManager;

import org.w3c.util.ArrayDictionary;
import org.w3c.util.EmptyEnumeration;
import org.w3c.util.ThreadCache;

import org.w3c.jigsaw.http.Reply;
import org.w3c.jigsaw.http.Request;
import org.w3c.jigsaw.http.httpd;

import org.w3c.tools.resources.Attribute;
import org.w3c.tools.resources.AttributeHolder;
import org.w3c.tools.resources.AttributeRegistry;
import org.w3c.tools.resources.FramedResource;
import org.w3c.tools.resources.InvalidResourceException;
import org.w3c.tools.resources.IntegerAttribute;
import org.w3c.tools.resources.LongAttribute;
import org.w3c.tools.resources.ObjectAttribute;
import org.w3c.tools.resources.PropertiesAttribute;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.ResourceReference;
import org.w3c.tools.resources.ServerInterface;
import org.w3c.tools.resources.StringAttribute;

import org.w3c.www.http.HTTP;

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

public class ServletWrapper extends FramedResource
    implements ServletConfig {

    static final ThreadCache threadCache = new ThreadCache("servlet-runner");

    /**
     * The overall thread cache size
     */
    static int global = 0;

    /**
     * The instance amount of the cache size
     */
    private int local = 0;
   
    /**
     * Tunes the thread cache.
     *
     * @param capacity  the required cache capacity of this instance
     */
    private final void tuneCache(int capacity) {
        int update = capacity - local;
        if (update != 0) {
            local = capacity;
            synchronized(threadCache) {
                global = Math.max(global + update, 0);
          threadCache.setCachesize(global);
                if (debug) {
                    System.out.println("cacheupdate local=" + local +
                                       " global=" + global);
    }
      }
  }
    }

    static {
  threadCache.setCachesize(global);
  threadCache.setGrowAsNeeded(true);
  // threadCache.setIdleTimeout(86400000);
  threadCache.initialize();
    }
   
    protected class TimeoutManager implements EventHandler {

  private Object timer     = null;
  private httpd  server    = null;

  /**
   * Handle timer events.
   * @param data The timer closure.
   * @param time The absolute time at which the event was triggered.
   * @see org.w3c.tools.timers.EventManager
   * @see org.w3c.tools.timers.EventHandler
   */
  public void handleTimerEvent(Object data, long time) {
      synchronized (this) {
    timer = null;
      }
            // FIXME, each servlet instance available should have its
      // individual timeout manager.
            // Thus, resources could be released as required. This mechanism
      // would opt for a more fine grained servlet instance management.
      destroyServlet();
  }

  private synchronized void setTimer(long ms) {
      if ( timer != null ) {
    server.timer.recallTimer(timer) ;
    timer = null ;
      }
      timer = server.timer.registerTimer(ms, this, null);
  }

  protected void restart() {
      start();
  }

  protected void start() {
      long timeout = getServletTimeout();
      if (timeout != -1)
    setTimer(timeout);
  }

  protected synchronized void stop() {
      if ( timer != null ) {
    server.timer.recallTimer(timer);
    timer = null;
      }
  }
 
  TimeoutManager(httpd server) {
      this.server = server;
  }

    }


    // used to pass the runner as a state
    public static final String RUNNER = "org.w3c.jigsaw.servlet.runner";
    // used to signal the end of the servlet in the Reply
    public static final String ENDED = "org.w3c.jigsaw.servlet.ended";

    protected TimeoutManager timeoutManager = null;
   
    // protected int connections = 0;

    protected final static boolean debug = false;

    /**
     * Attributes index - The servlet class name.
     */
    protected static int ATTR_SERVLET_CLASS = -1 ;
    /**
     * Attributes index - The servlet timeout
     */
    protected static int ATTR_SERVLET_TIMEOUT = -1 ;
    /**
     * Attributes index - The servlet maxinstances for single thread model
     * servlet instance pool size limitation, tk, 20.10.2001
     */
    protected static int ATTR_SERVLET_INSTANCEMAX = -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;
    /**
     * Attribute index - Our parent-inherited session context.
     */
    protected static int ATTR_SESSION_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.JigsawServletContext",
        null,
        Attribute.DONTSAVE);
  ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
  // Our session context:
  a = new ObjectAttribute("session-context",
           "org.w3c.jigsaw.servlet.JigsawHttpSessionContext",
        null,
        Attribute.DONTSAVE);
  ATTR_SESSION_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
  // The servlet timeout:
  a = new LongAttribute("servlet-timeout",
            null,
            Attribute.EDITABLE);
  ATTR_SERVLET_TIMEOUT = AttributeRegistry.registerAttribute(cls, a);
  // The servlet maxinstances:
  a = new IntegerAttribute("servlet-instancemax",
         null,
         Attribute.EDITABLE);
  ATTR_SERVLET_INSTANCEMAX = AttributeRegistry.registerAttribute(cls, a);
    }

    /**
     * Using a limited pool of servlet instances instead of a single
     * instance, tk, 22.10.2001
     * protected Servlet servlet = null;
     */
    protected final ServletPool servletPool = new ServletPool();
   
    /**
     * Is our servler initialized ?
     */
    protected boolean inited = false;
   
    /**
     * The Path where we can find the servlet class file.
     */
    public File getServletDirectory() {
  ResourceReference rr = getParent();
  if (rr != null) {
      try {
    Resource parent = rr.lock();
    if (parent.definesAttribute("directory"))
        return (File) parent.getValue("directory", null);
      } catch(InvalidResourceException ex) {
    ex.printStackTrace();
      } finally {
    rr.unlock();
      }
  }
  return null;
    }

    /**
     * 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() {
  ServletContext ctxt =
      (ServletContext) getValue(ATTR_SERVLET_CONTEXT, null);
  return ctxt;
    }

    public JigsawHttpSessionContext getSessionContext() {
  return (JigsawHttpSessionContext) getValue(ATTR_SESSION_CONTEXT, null);
    }

    protected long getServletTimeout() {
  long timeout = getLong(ATTR_SERVLET_TIMEOUT, 0);
  if (timeout == 0) {
      JigsawServletContext ctxt =
    (JigsawServletContext) getServletContext();
      timeout = ctxt.getServletTimeout();
  }
  return timeout;
    }

    protected int getInstanceMax() {
        // added for single thread model
  // servlet instance pool size limitation, tk, 20.10.2001
  int instancemax = getInt(ATTR_SERVLET_INSTANCEMAX, 0);
       
        if (instancemax == 0) {
      JigsawServletContext ctxt =
    (JigsawServletContext) getServletContext();
      instancemax = ctxt.getServletInstanceMax();
  }
        return instancemax;
    }

    protected void invalidateAllSession() {
  if (debug) {
      System.out.println("Invalidate All Session...");
  }
  JigsawHttpSessionContext ctxt = getSessionContext();
  Enumeration e = ctxt.getIds();
  while (e.hasMoreElements()) {
      ctxt.getSession((String)e.nextElement()).invalidate();
  }
    }

    /**
     * Check the servlet class, and try to initialize it.
     * @exception ClassNotFoundException if servlet class can't be found.
     * @exception ServletException if servlet can't be initialized.
     */
    protected void checkServlet()
  throws ClassNotFoundException, ServletException
    {
  synchronized(servletPool) {
      // tk, 20.10.2001, synchronized loading is necessary at
      // least for the non-SingleThreadModel
      boolean classmodified =
    getLocalServletLoader().classChanged(getServletClass());
     
      if ((!inited) ||
    classmodified ||
    // (servlet.getClass() !=
    //    getLocalServletLoader().loadClass(getServletClass()))
    (servletPool.getLoadedClass() !=
     getLocalServletLoader().loadClass(getServletClass()))
    ) {
    inited = launchServlet();
      }
  }
    }

    protected boolean isInited() {
  return inited;
    }

    /**
     * Check and eventually load the servlet we are wrapping.
     * This method was added for replacing getServlet() during access checks
     * in order to do perform checks without accessing a servlet instance.
     * @return true if and only if the sevlet has successfully been loaded
     */
    public boolean isServletLoaded() {
  try {
      checkServlet();
  } catch (Exception ex) {
      if (debug) {
    ex.printStackTrace();
      }
  }
  return inited;
    }
   
    /**
     * Helper class for managing a limited pool of servlet instances,
     * tk, 20.10.2001
     * For the SingleThreadModel instance are created as required and
     * retained up to a specified limit.
     * For the non-SingleThreadModel one instance only is created
     * (in accordance with the servlet spec).
     * The first instance initializes the pool with its pars-pro-toto
     * class attributes.
     */
    private class ServletPool implements EventHandler {
       
        // minimum wait time before tuning
        private static final int MINWAIT = 5000;
       
        // maximum delay for freeing instances
        private int idletime = MINWAIT;
       
        // earliest next tuning time (when decreasing)
        private long nexttime = -1L;
       
        // maximum pool size
        private int maximum = 0;
       
        // current pool capacity
        private int capacity = 0;
       
        // current pool usage level
        private int usage = 0;
       
        // list of servlet instances
        private final ArrayList pool = new ArrayList();

        // indicator for SingleThreadModel
        private boolean singleThreaded = false;
       
        // common class of the servlet pool
        private Class loadedClass = null;
       
        // the pool cleanup tuning event
        private Object cleanup = null;
       
        /**
         * Indicates, whether this pool needs to be initialized.
         */
        private final boolean requiresInitialization() {
            return ((!inited)||(capacity < 1)||(loadedClass == null));
        }
       
        /**
         * method for exporting the class common to all loaded servlet
   * instances
         * @return common loaded servlet class
         */
        protected Class getLoadedClass() {
            return loadedClass;
        }
       
        /**
         * method for adding a fresh servlet instance to the pool
   * (using external synchronization)
         * @param servlet the instance to be added to the pool
         * @return <strong>true</strong> if servlet was successfully added
         */
        protected boolean add(Servlet servlet) {
            if (requiresInitialization()) {
    singleThreaded = (servlet instanceof SingleThreadModel);
    if (singleThreaded) maximum = getInstanceMax();
    else maximum = 1;
                if (maximum > 0) {
                    pool.ensureCapacity(maximum);
                }
    loadedClass = servlet.getClass();
            }
            if ((maximum < 1)||(capacity < maximum)) {
    boolean success = pool.add(servlet);
                capacity = pool.size();
                return success;
            }
            else return false;
        }
       
        /**
         * non-blocking method for removing a servlet instance from the
   * pool (using external synchronization)
         * @return a removed servlet instance or null if the pool is empty
         */
        protected Servlet remove() {
            if ((capacity > 0)&&
                ((usage < 1)||(singleThreaded&&(capacity > usage)))) {
                Servlet servlet = (Servlet)pool.remove(0);
                capacity = pool.size();
                if (capacity < 1) {
                    loadedClass = null;
                }
                return servlet;
            }
            else return null;
        }
       
        /**
         * Uses a final timer for cleaning up the pool.
         *
         * @param wait        wait interval for the next cleaning action (non-positive to stop cleaning)
         */
        protected void clean(long wait) {
            // do something for the end
            EventManager manager = ((httpd)getServer()).timer;
            if (cleanup != null) {
                manager.recallTimer(cleanup);
            }
            if (wait > 0L) {
                cleanup = manager.registerTimer(wait, this, null);
            }
        }
       
        /**
         * Tunes this pool (and also the thread cache).
         * <p>
         * This method implements a simple stragegy, which might
         * be replaced by a better one or just NOP. In other words,
         * the method has no functional importance apart from
         * managing resource efficiency.
         *
         * @param increasing    indicates increasing usage
         */
        protected void tune(boolean increasing) {
            int capacity2 = (capacity >> 1);
            if (increasing) {
                if (usage > capacity2) {
                    nexttime = -1L;
                    if (usage >= capacity) {
                        tuneCache(capacity);             
                    }
                }
            }
            else {
                   if (usage < capacity2) {
                       long now = System.currentTimeMillis();
                       if (nexttime < 0) {
                           nexttime = now + idletime;
                           if (debug)
                               System.out.println("considering usage=" + usage +
                                                   " next=" + capacity2 +
                                                   " delay=" + idletime);
                       }
                       else {
                              long wait = nexttime - now;
                              if (wait <= 0L) {
                                  if (singleThreaded) {
                                      for (int i = capacity; i > capacity2; i--) {
                                           Servlet unused = remove();
                                           if (unused != null) {
                                               ClassLoader loader = switchContext(unused);
                                               try {
                                                     unused.destroy();
                                               }
                                               finally {
                                                         resetContext(loader);
                                               }
                                           }
                                           else break;
                                      }
                                  }
                                  else capacity = capacity2;
                                  nexttime = -1L;
                                  tuneCache(capacity);             
                                  if (debug)
                                      System.out.println("reducing usage=" + usage +
                                                         (singleThreaded ? " real" : " virtual") +
                                                         " capacity=" + capacity +
                                                         " maximum=" + maximum +
                                                         " thread=" + Thread.currentThread().getName());
                              }
                       }
                       
                       if (usage < 1) {
                           // buy an insurance
                           if (debug)
                               System.out.println("finalizing delay=" + idletime);
                           clean((idletime << 1));
                       }
                   }
            }
        }
       
  /**
   * Handle the cleanup event.
         *
   * @param data  The timer closure.
   * @param time  The absolute time at which the event was triggered.
   */
  public synchronized void handleTimerEvent(Object data, long time) {
            cleanup = null;
            if (usage < 1) {
               tune(false);
            }
  }
       
        /**
         * blocking method for accessing a servlet instance from the pool
         * @exception ServletException thrown if the pool is not properly
   * initialized
         */
        protected synchronized Servlet takeServlet() throws ServletException, IOException {
            if (requiresInitialization()) {
                throw new ServletException("Accessing servlet without"+
                               " initialization.");
            }
            if (singleThreaded) {
                try {
                      while (true) {
                            if (usage < capacity) {
                                Servlet servlet = (Servlet)pool.get(usage++);
                                tune(true);
                                if (debug)
                                    System.out.println("taking usage=" + usage +
                                                       " real capacity=" + capacity +
                                                       " maximum=" + maximum +
                                                       " servlet=" + servlet.hashCode() +
                                                       " thread=" + Thread.currentThread().getName());
                                return servlet;
                            } else {
                                if ((maximum < 1)||(capacity < maximum)) {
                                    if (launchServlet(loadedClass)) {
                                        notifyAll();
                                        Thread.currentThread().yield();
                                        // give previous waiters a chance
                                    } else {
                                        if (debug)
                                            System.out.println("waiting thread=" + Thread.currentThread().getName());
                                        wait();
                                    }
                                } else {
                                    if (debug)
                                        System.out.println("waiting thread=" + Thread.currentThread().getName());
                                    wait();
                                }
                            }
                      }
                }
    catch (InterruptedException ex) {
                        throw new IOException("Waiting for a free servlet"+
                                              " instance interrupted.");
    }
            } else {
                     // One instance only is used in non single thread case
                     // (cf. servlet api for details)
                     usage++;
                     capacity = Math.max(capacity, usage);
                     Servlet servlet = (Servlet)pool.get(0);
                     tune(true);
                     if (debug)
                         System.out.println("taking usage=" + usage +
                                            " virtual capacity=" + capacity +
                                            " maximum=" + maximum +
                                            " servlet=" + servlet.hashCode() +
                                            " thread=" + Thread.currentThread().getName());
                     return servlet;
            }
        }
       
        /**
         * method for releasing a servlet instance into the pool after work
         * @param servlet the instance to be returned to the pool
         * @param duration  the request duration
         * @exception ServletException thrown if pool is not properly
   * initialized
         */
        protected synchronized void releaseServlet(Servlet servlet, int duration)
      throws ServletException {
            if (requiresInitialization()) {
     throw new ServletException("Releasing servlet without"+
             " initialization.");
            }
            idletime = Math.max(idletime, duration);
            if (usage > 0) {
    if (singleThreaded) {
        pool.set(--usage, servlet);
                    tune(false);
                    if (debug)
                        System.out.println("releasing usage=" + usage +
                                           " real capacity=" + capacity +
                                           " maximum=" + maximum +
                                           " servlet=" + servlet.hashCode() +
                                           " thread=" + Thread.currentThread().getName());
        notifyAll();
    } else {
        // In this case the servlet instance is shared, i.e.
        // we have a counting semaphore only.
        usage--;
                    tune(false);
                    if (debug)
                        System.out.println("releasing usage=" + usage +
                                           " virtual capacity=" + capacity +
                                           " maximum=" + maximum +
                                           " servlet=" + servlet.hashCode() +
                                           " thread=" + Thread.currentThread().getName());
    }
            } else {
    throw new ServletException("Incorrect servlet release"+
             " occurred.");
      }
        }
       
        /**
         * method for referencing a servlet instance of the pool
         * This method was added for backward compatibility in order to
   * support the deprecated getServlet() method,
         * which is structurally not applicable to the pool mechanism, i.e.
   * it always returns null in
         * case of the SingleThreadModel (in accordance with the current
   * servlet spec)
         * @return a servlet reference or null
         */
        protected synchronized Servlet getRepresentative() {
            if (requiresInitialization()) {
                return null;
            } else {
                        if (singleThreaded) {
          // FIXME, here we could also return pool[0],
          // which is defined but probably taken.
          // However, using pool[0] in a normal manner might
          // cause strange behavior
          // due to its single threaded design aspect.
          return null;
      } else {
          return (Servlet)pool.get(0);
      }
           }
        }
    }   
   
    protected class ServletRunner implements Runnable, EventHandler {
  Servlet srServlet = null;
  JigsawHttpServletRequest srReq = null;
  JigsawHttpServletResponse srResp = null;
  Thread t = null;
  private Object stimer = null;
  private httpd server = (httpd)getServer();
       
  /**
   * Handle timer events.
   * @param data The timer closure.
   * @param time The absolute time at which the event was triggered.
   * @see org.w3c.tools.timers.EventManager
   * @see org.w3c.tools.timers.EventHandler
   */
  public void handleTimerEvent(Object data, long time) {
      signalTimeout();
  }

  protected void signalTimeout() {
      synchronized (this) {
    stimer = null;
      // }
      // as the request timeouted, we interrupt the runner's thread
      // (as this thread may be the cause of the timeout)
      // synchronized (this) {
    if (t != null) {
        if (debug)
      System.out.println("Killing " +t);
        t.interrupt();
    }
      }
  }

  public void run() {
      synchronized (this) {
    t = Thread.currentThread();
      }
            long start = System.currentTimeMillis();               
      stimer = server.timer.registerTimer(server.getRequestTimeOut(),
            this, null);
            if (debug)
                System.out.println("running servlet=" + srServlet.hashCode() +
                                   " thread=" + Thread.currentThread().getName());
                       
      // synchronization object
      Object o = null;
      try {
               
                Reply reply = srResp.getReply();
                if (reply != null) {
                        o = reply.getState(JigsawHttpServletResponse.MONITOR);
                }
               
                ClassLoader loader = switchContext(srServlet);
                try {
            srServlet.service(srReq , srResp);
                }
                finally {
                            resetContext(loader);
                }
               
    // processing done, release the servlet
                try {
                    int duration = (int)Math.min(System.currentTimeMillis() - start, Integer.MAX_VALUE);
        servletPool.releaseServlet(srServlet, duration);
    } finally {
        srServlet = null;
                }
    // and remove the timer
    if ( stimer != null ) {
        server.timer.recallTimer(stimer);
        stimer = null;
    }
    srResp.flushStream(true);
      } catch (UnavailableException uex) {
    String message = null;
    srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
    if (uex.isPermanent()) {
        message = "<h2>The servlet is permanently "+
      "unavailable :</h2>"+
      "Details: <b>"+uex.getMessage()+"</b>";
        try {
      srResp.sendError(HTTP.SERVICE_UNAVAILABLE, message);
        } catch (IOException ioex) {
      // not much to do now...
        }
    } else {
        int delay = uex.getUnavailableSeconds();
        if (delay > 0) {
      message = "<h2>The servlet is temporarily "+
          "unavailable :</h2>"+
          "Delay : "+delay+
          " seconds<br><br>Details: <b>"+
          uex.getMessage()+"</b>";
      srResp.getReply().setRetryAfter(delay);
      try {
          srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
      } catch (IOException ioex) {
          // not much to do now...
      }
        } else {
      message = "<h2>The servlet is temporarily "+
          "unavailable :</h2>"+
          "Details: <b>"+uex.getMessage()+
          "</b>";
      try {   
          srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
      } catch (IOException ioex) {
          // not much to do now...
      }
        }
    }
      } catch (Exception ex) {
    if (debug) {
        ex.printStackTrace();
    }
    if (srResp.isStreamObtained()) {
        try {
      srResp.flushStream(false);
      OutputStream os = srResp.getRawOutputStream();
      if (os != null) {
          synchronized (os) {
        PrintWriter pw = new PrintWriter(os);
        ex.printStackTrace(pw);
        pw.flush();
          }
      }
      srResp.flushStream(true);
        } catch (IOException ioex) {}
    } else {
        try {
      srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
           "Servlet has thrown exception:" +
           ex.toString());
        } catch (IOException ioex) {
      // no stream to write on, fail silently
        }
    }
      } finally {
    if ( stimer != null ) {
        server.timer.recallTimer(stimer);
        stimer = null;
    }
                if (srServlet != null) {
        try {
                        int duration = (int)Math.min(System.currentTimeMillis() - start, Integer.MAX_VALUE);
      servletPool.releaseServlet(srServlet, duration);
        }
        catch (ServletException ex) {
      // ignore
        }
                }
    // release the monitor waiting for the end of the reply setup
    if (o != null) {
        synchronized (o) {
      o.notifyAll();
        }
    }
    srServlet = null;
    srReq = null;
    // adds the END state
    Reply r = srResp.getReply();
    if (r != null) {
        r.setState(ENDED, new Object());
    }
    srResp = null;
      }
  }
 
  ServletRunner(Servlet servlet,
          JigsawHttpServletRequest request,
          JigsawHttpServletResponse response) {
      srServlet = servlet;
      srReq = request;
      srResp = response;
   }
    }

    protected void service(Request request, Reply reply)
  throws ServletException, IOException {
       
      JigsawHttpServletResponse jRes = null;
      JigsawHttpServletRequest jReq = null;
      /* modified due to servlet pooling, tk, 20.10.2001
         if (servlet instanceof SingleThreadModel) {
         synchronized (this) {
         jRes = new JigsawHttpServletResponse(request, reply);
         jReq = new JigsawHttpServletRequest(servlet,
         request,
         jRes,
         getSessionContext());
         jRes.setServletRequest(jReq);
         try {
         connections++;
         // FIXME we should reuse a thread rather than
         // reallocating one for every hit
         ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
         reply.setState(RUNNER, runner);
         runner.start();
         } finally {
         connections--;
         }
         }
         } else {
      */
      jRes = new JigsawHttpServletResponse(request, reply);
            Servlet servlet = servletPool.takeServlet();
            // accessing a fresh or sharable instance, tk, 21.10.2001
      // jReq = new JigsawHttpServletRequest(servlet,
      JigsawServletContext jco =
                        (JigsawServletContext) getServletContext();
      jReq = new JigsawHttpServletRequest(servlet,
            jco,
            request,
            jRes,
            getSessionContext());
      jRes.setServletRequest(jReq);
      // try {
        //       connections++;
      // FIXME we should reuse a thread rather than
      // reallocating one for every hit
      // reallocating one for every hit
      ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
      reply.setState(RUNNER, runner);
            threadCache.getThread(runner, true);
      // runner.start();
      // } finally {
      //     connections--;
      // }
      /* } */
      timeoutManager.restart();
    }

    /**
     * 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);
    }

    protected void setValueOfSuperClass(int idx, Object value) {
  super.setValue(idx, value);
    }

    /**
     * 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)) ||
      (idx == ATTR_PARAMETERS)) {
      try {
                synchronized(servletPool) {
                    // synchronization added, tk, 20.10.2001
            inited = launchServlet();
                }
      } catch (Exception ex) {
    String msg = ("unable to set servlet class \""+
            getServletClass()+
            "\" : "+
            ex.getMessage());
    getServer().errlog(msg);
      }
  } if (idx == ATTR_SERVLET_TIMEOUT) {
      timeoutManager.restart();
  }
    }

    /**
     * Destroy the servlet we are wrapping.
     */
    protected void destroyServlet() {
  // if ((servlet != null) && (connections < 1)) {
  RuntimeException rex = null;
  synchronized(servletPool) {
      Servlet servlet = servletPool.remove();
      while (servlet != null) {
        try {
                       ClassLoader loader = switchContext(servlet);
                       try {
              servlet.destroy();
                    // servlet = null;
                       }
                       finally {
                                    resetContext(loader);
                       }
    }
    catch (RuntimeException ex) {
      if (rex != null) {
          rex = ex;
      }
    }
    finally {
      servlet = servletPool.remove();
    }
      }
      inited = (servletPool.getLoadedClass() != null);
            if (!inited) {
                servletPool.clean(0L);
                tuneCache(0);
            }
  }
  if (rex != null) throw rex;
  // }
    }

    /**
     * Get the servlet we are wrapping.
     * @return A servlet instance, if the servlet is alredy running,
     * <strong>null</strong> otherwise.
     */
    public Servlet getServlet() {
  try {
      checkServlet();
  } catch (Exception ex) {
      if (debug)
    ex.printStackTrace();
  }
  return servletPool.getRepresentative();
    }
   
    /**
     * Sets the context classloader for the specified servlet class and
     * returns its predecessor or <code>null</code>.
     *
     * @return  the previous context classloader if any
     */
    private static final ClassLoader switchContext(Servlet servlet) {
        if (servlet != null) {
            ClassLoader newContextClassLoader = servlet.getClass().getClassLoader();
            Thread handler = Thread.currentThread();
            ClassLoader oldContextClassLoader = handler.getContextClassLoader();
            if (newContextClassLoader != oldContextClassLoader) {
                handler.setContextClassLoader(newContextClassLoader);
                return oldContextClassLoader;
            } else return null;
        }
        else return null;
    }
   
    /**
     * Resets the context classloader if applicable.
     *
     * @param loader    the previous context classloader or <code>null</code>
     */
    private static final void resetContext(ClassLoader loader) {
        Thread.currentThread().setContextClassLoader(loader);
    }
   
    /**
     * 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.
     * @exception ServletException if servlet can't be initialized.
     */

    protected boolean launchServlet(Class cls)
  throws ServletException
  {
      if (debug) {
    System.out.println("launching Servlet: "+getServletName());
      }
      Servlet servlet = null;
      try {
    servlet = (Servlet) cls.newInstance();
                ClassLoader loader = switchContext(servlet);
                try {
                        servlet.init((ServletConfig) this);
                }
                finally {
                            resetContext(loader);
                }
    timeoutManager.restart();
    // modified for servlet pool and executed in a context
    // synchronized on the servlet pool, tk, 20.10.2001
    return servletPool.add(servlet);
      } catch (IllegalAccessException ex) {
    String msg = ("Illegal access during servlet instantiation, "+
            ex.getClass().getName()+": "+
            ex.getMessage());
    if ( debug ) {
        ex.printStackTrace();
    }
    getServer().errlog(this, msg);
    // return false;
    throw new ServletException(msg, ex);
      } catch (InstantiationException iex) {
    String msg = ("unable to instantiate servlet, "+
            iex.getClass().getName()+": "+
            iex.getMessage());
    if ( debug ) {
        iex.printStackTrace();
    }
    getServer().errlog(this, msg);
    // return false;
    throw new ServletException(msg, iex);
      } catch (ServletException nex) {
    String msg = ("Error while initializing servlet");
    getServer().errlog(this, msg);
    if ( debug ) {
        nex.printStackTrace();
    }
    getServer().errlog(this, msg);
    // return false;
    throw nex;
      } catch (Exception oex) {
    String msg = ("Error while loading servlet");
    getServer().errlog(this, msg);
    if ( debug ) {
        oex.printStackTrace();
    }
    getServer().errlog(this, msg);
    // return false;
                throw new ServletException(msg, oex);
            }
  }

    /**
     * Check if the Servletclass wrapped is a Servlet class without
     * initializing it. (not the same than checkServlet).
     * used by the ServletIndexer.
     * @see org.w3c.jigsaw.servlet.ServletIndexer
     * @return A boolean.
     */
    protected boolean isWrappingAServlet() {
  String clsname = getServletClass();
  if ( clsname == null ) {
      return false;
  }
  Class c = null;
  try {
      c = getLocalServletLoader().loadClass(clsname, true);
      Object o = c.newInstance();
      return (o instanceof Servlet);
  } catch (Exception ex) {
      return false;
  }
    }

    /**
     * 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.
     * @exception ClassNotFoundException if servlet class can't be found.
     * @exception ServletException if servlet can't be initialized.
     */

    protected boolean launchServlet()
  throws ClassNotFoundException, ServletException
  {
      // Get and check the servlet class:
      // if ( servlet != null )
      destroyServlet();
      if (inited) {
    String msg = "relaunching servlet failed due to incomplete \""
        + getServletClass() + "\" cleanup.";
    getServer().errlog(this, msg);
    return false;
      } else {
    // Load appropriate servlet class:
    String clsname = getServletClass();
    if ( clsname == null ) {
        getServer().errlog(this, "no servlet class attribute"+
               " defined.");
        return false;
    } else {
        Class c = null;
        try {
      if (getLocalServletLoader().classChanged(clsname)) {
          createNewLocalServletLoader(true);
          invalidateAllSession();
      }
      c = getLocalServletLoader().loadClass(clsname, true);
        } catch (ClassNotFoundException ex) {
      String msg = ("unable to find servlet class \""+
              getServletClass()+"\"");
      getServer().errlog(msg);
      // re throw the exception
      throw ex;
        }
        return (c != null) ? launchServlet(c) : false;
    }
      }
  }
   
    public boolean acceptUnload() {
  // return (servlet == null);
        return (!inited);
    }
   
    public void notifyUnload() {
  if (timeoutManager != null) {
      timeoutManager.stop();
  }
  destroyServlet();
    }

    /**
     * Get or create a suitable LocalServletLoader instance to load
     * that servlet.
     * @return A LocalServletLoader instance.
     */
    // singleton synchronized already performed at the servlet context.
    // protected synchronized AutoReloadServletLoader getLocalServletLoader() {
    protected AutoReloadServletLoader getLocalServletLoader() {
  JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
  return ctxt.getLocalServletLoader();
    }

    protected AutoReloadServletLoader createNewLocalServletLoader(boolean
                  keepold)
    {
  JigsawServletContext ctxt = (JigsawServletContext)
                                          getServletContext();
  return ctxt.createNewLocalServletLoader(keepold);
    }

    /**
     * Returns the name of this servlet instance.
     * The name may be provided via server administration, assigned in the
     * web application deployment descriptor, or for an unregistered (and thus
     * unnamed) servlet instance it will be the servlet's class name.
     * @return the name of the servlet instance
     */
    public String getServletName() {
  return getIdentifier();
    }

    /**
     * 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);
  // connections = 0;

  if (getServletContext() != null) {
      timeoutManager = new TimeoutManager((httpd)getServer());
      timeoutManager.start();
  }

  try {
      registerFrameIfNone("org.w3c.jigsaw.servlet.ServletWrapperFrame",
        "servlet-wrapper-frame");
  } catch (Exception ex) {
      ex.printStackTrace();
  }
    }
}
TOP

Related Classes of org.w3c.jigsaw.servlet.ServletWrapper$ServletRunner

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.