Annotation of java/classes/org/w3c/jigsaw/servlet/ServletWrapper.java, revision 1.67

1.1       abaird      1: // ServletWrapper.java
1.67    ! ylafon      2: // $Id: ServletWrapper.java,v 1.66 2002/09/16 18:36:56 ylafon Exp $
1.1       abaird      3: // (c) COPYRIGHT MIT and INRIA, 1996.
                      4: // Please first read the full copyright statement in file COPYRIGHT.html
                      5: 
1.18      bmahe       6: package org.w3c.jigsaw.servlet;
1.1       abaird      7: 
1.51      ylafon      8: import java.io.File;
                      9: import java.io.IOException;
1.64      ylafon     10: import java.io.OutputStream;
1.51      ylafon     11: import java.io.PrintStream;
1.64      ylafon     12: import java.io.PrintWriter;
1.51      ylafon     13: 
                     14: import java.util.Enumeration;
1.59      ylafon     15: import java.util.LinkedList;
                     16: import java.util.NoSuchElementException;
1.51      ylafon     17: 
                     18: import javax.servlet.Servlet;
                     19: import javax.servlet.ServletConfig;
                     20: import javax.servlet.ServletContext;
                     21: import javax.servlet.ServletException;
                     22: import javax.servlet.SingleThreadModel;
1.56      ylafon     23: import javax.servlet.UnavailableException;
1.51      ylafon     24: 
                     25: import org.w3c.tools.timers.EventHandler;
                     26: import org.w3c.tools.timers.EventManager;
                     27: 
                     28: import org.w3c.util.ArrayDictionary;
                     29: import org.w3c.util.EmptyEnumeration;
1.62      ylafon     30: import org.w3c.util.ThreadCache;
1.51      ylafon     31: 
                     32: import org.w3c.jigsaw.http.Reply;
                     33: import org.w3c.jigsaw.http.Request;
                     34: import org.w3c.jigsaw.http.httpd;
                     35: 
                     36: import org.w3c.tools.resources.Attribute;
                     37: import org.w3c.tools.resources.AttributeHolder;
                     38: import org.w3c.tools.resources.AttributeRegistry;
                     39: import org.w3c.tools.resources.FramedResource;
                     40: import org.w3c.tools.resources.InvalidResourceException;
1.59      ylafon     41: import org.w3c.tools.resources.IntegerAttribute;
1.51      ylafon     42: import org.w3c.tools.resources.LongAttribute;
                     43: import org.w3c.tools.resources.ObjectAttribute;
                     44: import org.w3c.tools.resources.PropertiesAttribute;
                     45: import org.w3c.tools.resources.Resource;
                     46: import org.w3c.tools.resources.ResourceReference;
                     47: import org.w3c.tools.resources.ServerInterface;
                     48: import org.w3c.tools.resources.StringAttribute;
1.1       abaird     49: 
1.56      ylafon     50: import org.w3c.www.http.HTTP;
                     51: 
1.2       abaird     52: /**
                     53:  * @author Alexandre Rafalovitch <alex@access.com.au>
                     54:  * @author Anselm Baird-Smith <abaird@w3.org>
1.18      bmahe      55:  * @author Benoit Mahe <bmahe@w3.org>
1.2       abaird     56:  */
                     57: 
1.40      bmahe      58: public class ServletWrapper extends FramedResource 
                     59:     implements ServletConfig
1.1       abaird     60: {
                     61: 
1.62      ylafon     62:     static ThreadCache threadCache = null;
                     63: 
                     64:     static {
                     65:        threadCache = new ThreadCache("servlet-runner");
                     66:        threadCache.setCachesize(5);
                     67:        threadCache.setGrowAsNeeded(true);
                     68:        threadCache.setIdleTimeout(86400000);
                     69:        threadCache.initialize();
                     70:     }
                     71: 
1.40      bmahe      72:     protected class TimeoutManager implements EventHandler {
                     73: 
                     74:        private Object timer     = null;
                     75:        private httpd  server    = null;
                     76: 
                     77:        /**
                     78:         * Handle timer events. 
                     79:         * @param data The timer closure.
                     80:         * @param time The absolute time at which the event was triggered.
                     81:         * @see org.w3c.tools.timers.EventManager
                     82:         * @see org.w3c.tools.timers.EventHandler
                     83:         */
                     84:        public synchronized void handleTimerEvent(Object data, long time) {
                     85:            timer = null;
1.60      ylafon     86:             // FIXME, each servlet instance available should have its 
                     87:            // individual timeout manager.
                     88:             // Thus, resources could be released as required. This mechanism
                     89:            // would opt for a more fine grained servlet instance management.
1.40      bmahe      90:            destroyServlet();
                     91:        }
                     92: 
                     93:        private synchronized void setTimer(long ms) {
                     94:            if ( timer != null ) {
                     95:                server.timer.recallTimer(timer) ;
                     96:                timer = null ;
                     97:            }
                     98:            timer = server.timer.registerTimer(ms, this, null);
                     99:        }
                    100: 
                    101:        protected void restart() {
                    102:            start();
                    103:        }
                    104: 
                    105:        protected void start() {
1.45      bmahe     106:            long timeout = getServletTimeout();
                    107:            if (timeout != -1)
1.59      ylafon    108:                setTimer(timeout);
1.40      bmahe     109:        }
                    110: 
                    111:        protected synchronized void stop() {
                    112:            if ( timer != null ) {
                    113:                server.timer.recallTimer(timer) ;
                    114:                timer = null;
                    115:            }
                    116:        }
                    117:        
                    118:        TimeoutManager(httpd server) {
                    119:            this.server = server;
                    120:        }
                    121: 
                    122:     }
                    123: 
1.57      ylafon    124:     public static final String RUNNER = "org.w3c.jigsaw.servlet.runner";
                    125: 
1.40      bmahe     126:     protected TimeoutManager timeoutManager = null;
                    127: 
1.60      ylafon    128:     // protected int connections = 0;
1.46      bmahe     129: 
1.40      bmahe     130:     protected final static boolean debug = false;
1.9       bmahe     131: 
1.20      bmahe     132:     /**
                    133:      * Attributes index - The servlet class name.
                    134:      */
                    135:     protected static int ATTR_SERVLET_CLASS = -1 ;
                    136:     /**
1.40      bmahe     137:      * Attributes index - The servlet timeout
                    138:      */
                    139:     protected static int ATTR_SERVLET_TIMEOUT = -1 ;
                    140:     /**
1.60      ylafon    141:      * Attributes index - The servlet maxinstances for single thread model
                    142:      * servlet instance pool size limitation, tk, 20.10.2001
1.59      ylafon    143:      */
                    144:     protected static int ATTR_SERVLET_INSTANCEMAX = -1 ;
                    145:     /**
1.20      bmahe     146:      * Attribute index - The init parameters for that servlet.
                    147:      */
                    148:     protected static int ATTR_PARAMETERS = 1;
                    149:     /**
                    150:      * Attribute index - Our parent-inherited servlet context.
                    151:      */
                    152:     protected static int ATTR_SERVLET_CONTEXT = -1;
                    153:     /**
1.28      bmahe     154:      * Attribute index - Our parent-inherited session context.
                    155:      */
                    156:     protected static int ATTR_SESSION_CONTEXT = -1;
1.20      bmahe     157: 
                    158:     static {
                    159:        Attribute a   = null ;
                    160:        Class     cls = null ;
                    161:        try {
                    162:            cls = Class.forName("org.w3c.jigsaw.servlet.ServletWrapper") ;
                    163:        } catch (Exception ex) {
                    164:            ex.printStackTrace();
                    165:            System.exit(0);
                    166:        }
                    167:        // The servlet class attribute.
                    168:        a = new StringAttribute("servlet-class"
1.1       abaird    169:                                , null
1.20      bmahe     170:                                , Attribute.EDITABLE | Attribute.MANDATORY) ;
                    171:        ATTR_SERVLET_CLASS = AttributeRegistry.registerAttribute(cls, a) ;
                    172:        // This servlet init parameters
                    173:        a = new PropertiesAttribute("servlet-parameters"
                    174:                                    , null
                    175:                                    , Attribute.EDITABLE);
                    176:        ATTR_PARAMETERS = AttributeRegistry.registerAttribute(cls, a);
                    177:        // Our servlet context:
                    178:        a = new ObjectAttribute("servlet-context",
1.24      bmahe     179:                                "org.w3c.jigsaw.servlet.JigsawServletContext",
1.20      bmahe     180:                                null,
                    181:                                Attribute.DONTSAVE);
                    182:        ATTR_SERVLET_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.28      bmahe     183:        // Our session context:
                    184:        a = new ObjectAttribute("session-context",
1.60      ylafon    185:                             "org.w3c.jigsaw.servlet.JigsawHttpSessionContext",
                    186:                                null,
                    187:                                Attribute.DONTSAVE);
1.28      bmahe     188:        ATTR_SESSION_CONTEXT = AttributeRegistry.registerAttribute(cls, a);
1.40      bmahe     189:        // The servlet timeout:
                    190:        a = new LongAttribute("servlet-timeout",
                    191:                              null,
                    192:                              Attribute.EDITABLE);
                    193:        ATTR_SERVLET_TIMEOUT = AttributeRegistry.registerAttribute(cls, a);
1.59      ylafon    194:        // The servlet maxinstances:
                    195:        a = new IntegerAttribute("servlet-instancemax",
1.60      ylafon    196:                                 null,
                    197:                                 Attribute.EDITABLE);
1.59      ylafon    198:        ATTR_SERVLET_INSTANCEMAX = AttributeRegistry.registerAttribute(cls, a);
1.20      bmahe     199:     }
1.51      ylafon    200: 
1.59      ylafon    201:     /** 
1.60      ylafon    202:      * Using a limited pool of servlet instances instead of a single 
                    203:      * instance, tk, 22.10.2001
1.59      ylafon    204:      * protected Servlet servlet = null;
1.20      bmahe     205:      */
1.59      ylafon    206:     protected ServletPool servletPool = new ServletPool(); 
                    207:     
1.20      bmahe     208:     /**
1.59      ylafon    209:      * Is our servler initialized ?
1.20      bmahe     210:      */
                    211:     protected boolean inited = false;
                    212: 
                    213:     /**
                    214:      * The Path where we can find the servlet class file.
                    215:      */
                    216:     public File getServletDirectory() {
1.25      bmahe     217:        ResourceReference rr = getParent();
                    218:        if (rr != null) {
                    219:            try {
                    220:                Resource parent = rr.lock();
                    221:                if (parent.definesAttribute("directory"))
                    222:                    return (File) parent.getValue("directory", null);
                    223:            } catch(InvalidResourceException ex) {
                    224:                ex.printStackTrace();
                    225:            } finally {
                    226:                rr.unlock();
                    227:            }
                    228:        }
                    229:        return null;
1.20      bmahe     230:     }
                    231: 
                    232:     /**
                    233:      * Servlet stub implementation - Get an init parameter value.
                    234:      */
                    235:     public synchronized String getInitParameter(String string) {
                    236:        ArrayDictionary d = getServletParameters();
                    237:        String          v = (d != null) ? (String) d.get(string) : null;
                    238:        return v;
                    239:     }
                    240: 
                    241:     /**
                    242:      * Servlet stub implementation - Get all init parameters.
                    243:      */
                    244:     public synchronized Enumeration getInitParameterNames() {
                    245:        // Convert init parameters to hashtable:
                    246:        ArrayDictionary d = getServletParameters();
                    247:        return (d != null) ? d.keys() : new EmptyEnumeration();
                    248:     }
                    249: 
                    250:     /**
                    251:      * Servlet stub implementation - Get that servlet context.
                    252:      */
                    253:     public ServletContext getServletContext() { 
1.52      bmahe     254:        ServletContext ctxt = 
                    255:            (ServletContext) getValue(ATTR_SERVLET_CONTEXT, null);
                    256:        return ctxt;
1.20      bmahe     257:     }
                    258: 
1.28      bmahe     259:     public JigsawHttpSessionContext getSessionContext() {
                    260:        return (JigsawHttpSessionContext) getValue(ATTR_SESSION_CONTEXT, null);
                    261:     }
                    262: 
1.40      bmahe     263:     protected long getServletTimeout() {
1.45      bmahe     264:        long timeout = getLong(ATTR_SERVLET_TIMEOUT, 0);
                    265:        if (timeout == 0) {
1.40      bmahe     266:            JigsawServletContext ctxt = 
                    267:                (JigsawServletContext) getServletContext();
                    268:            timeout = ctxt.getServletTimeout();
                    269:        }
                    270:        return timeout;
                    271:     }
                    272: 
1.60      ylafon    273:     protected int getInstanceMax() { 
                    274:         // added for single thread model 
                    275:        // servlet instance pool size limitation, tk, 20.10.2001
1.59      ylafon    276:        int instancemax = getInt(ATTR_SERVLET_INSTANCEMAX, 1);
                    277:        if (instancemax < 1) {
                    278:            JigsawServletContext ctxt = 
                    279:                (JigsawServletContext) getServletContext();
                    280:            instancemax = ctxt.getServletInstanceMax();
                    281:        }
1.60      ylafon    282:         if (instancemax < 1) {
                    283:            return 1;
                    284:         } else {
                    285:            return instancemax;
                    286:        }
1.59      ylafon    287:     }
                    288: 
1.36      bmahe     289:     protected void invalidateAllSession() {
1.60      ylafon    290:        if (debug) {
1.36      bmahe     291:            System.out.println("Invalidate All Session...");
1.60      ylafon    292:        }
1.36      bmahe     293:        JigsawHttpSessionContext ctxt = getSessionContext();
                    294:        Enumeration enum = ctxt.getIds();
1.60      ylafon    295:        while (enum.hasMoreElements()) {
1.36      bmahe     296:            ctxt.getSession((String)enum.nextElement()).invalidate();
1.60      ylafon    297:        }
1.36      bmahe     298:     }
                    299: 
1.37      bmahe     300:     /**
                    301:      * Check the servlet class, ans try to initialize it.
                    302:      * @exception ClassNotFoundException if servlet class can't be found.
                    303:      * @exception ServletException if servlet can't be initialized.
                    304:      */
1.36      bmahe     305:     protected void checkServlet() 
                    306:        throws ClassNotFoundException, ServletException
1.60      ylafon    307:        {
                    308:            synchronized(servletPool) { 
                    309:                 // tk, 20.10.2001, synchronized loading is necessary at 
                    310:                // least for the non-SingleThreadModel
                    311:                boolean classmodified = 
                    312:                    getLocalServletLoader().classChanged(getServletClass());
                    313: 
                    314:                if ((!inited) ||
                    315:                    classmodified ||
                    316:                    // (servlet.getClass() != 
                    317:                    //    getLocalServletLoader().loadClass(getServletClass()))
                    318:                    (servletPool.getLoadedClass() != 
                    319:                        getLocalServletLoader().loadClass(getServletClass()))
                    320:                    ) {
                    321:                    inited = launchServlet();
                    322:                }
1.36      bmahe     323:            }
1.60      ylafon    324:        }
1.20      bmahe     325: 
                    326:     protected boolean isInited() {
                    327:        return inited;
                    328:     }
                    329: 
1.59      ylafon    330:     /**
1.60      ylafon    331:      * Check and eventually load the servlet we are wrapping.
                    332:      * This method was added for replacing getServlet() during access checks
                    333:      * in order to do perform checks without accessing a servlet instance.
                    334:      * @return true if and only if the sevlet has successfully been loaded
                    335:      */
                    336:     public boolean isServletLoaded() {
                    337:        try {
                    338:            checkServlet();
                    339:        } catch (Exception ex) {
                    340:            if (debug) {
                    341:                ex.printStackTrace();
                    342:            }
                    343:        }
                    344:        return inited;
                    345:     }
                    346:     
                    347:     /**
                    348:      * Helper class for managing a limited pool of servlet instances, 
                    349:      * tk, 20.10.2001
                    350:      * For the SingleThreadModel instance are created as required and 
                    351:      * retained up to a specified limit.
                    352:      * For the non-SingleThreadModel one instance only is created 
                    353:      * (in accordance with the servlet spec).
                    354:      * The first instance initializes the pool with its pars-pro-toto
                    355:      * class attributes.
1.59      ylafon    356:      */
                    357:     private class ServletPool {
                    358:         
1.60      ylafon    359:         // maximum pool size
                    360:         private int maximum = 1;
                    361:         
1.59      ylafon    362:         // current pool capacity
                    363:         private int capacity = 0;
                    364:         
1.60      ylafon    365:         // current pool usage level
                    366:         private int usage = 0;
                    367:         
1.59      ylafon    368:         // list of servlet instances
1.60      ylafon    369:         private Servlet[] pool = null;
1.59      ylafon    370: 
                    371:         // indicator for SingleThreadModel
                    372:         private boolean singleThreaded = false;
                    373:         
                    374:         // common class of the servlet pool
                    375:         private Class loadedClass = null;
                    376: 
1.60      ylafon    377:         
1.59      ylafon    378:         /**
1.60      ylafon    379:          * method for exporting the class common to all loaded servlet 
                    380:         * instances
1.59      ylafon    381:          * @return common loaded servlet class
                    382:          */
                    383:         protected Class getLoadedClass() {
                    384:             return loadedClass;
                    385:         }
                    386:         
                    387:         /**
1.60      ylafon    388:          * method for adding a fresh servlet instance to the pool 
                    389:         * (using external synchronization)
1.59      ylafon    390:          * @param servlet the instance to be added to the pool
                    391:          */
                    392:         protected void add(Servlet servlet) {
1.60      ylafon    393:             if ((pool == null)||(loadedClass == null)) {
                    394:                singleThreaded = (servlet instanceof SingleThreadModel);
                    395:                if (singleThreaded) maximum = getInstanceMax(); 
                    396:                else maximum = 1;
                    397:                loadedClass = servlet.getClass();
                    398:                pool = new Servlet[maximum];
                    399:             }
                    400:             if (capacity < maximum) {
                    401:                pool[capacity++] = servlet;
1.59      ylafon    402:             }
                    403:         }
                    404:         
                    405:         /**
1.60      ylafon    406:          * non-blocking method for removing a servlet instance from the
                    407:         * pool (using external synchronization)
1.59      ylafon    408:          * @return a removed servlet instance or null if the pool is empty
                    409:          */
                    410:         protected Servlet remove() {
1.60      ylafon    411:            if (capacity > usage) {
                    412:                 Servlet servlet = pool[--capacity];
                    413:                 if (capacity < 1) {
                    414:                    pool = null;
                    415:                    loadedClass = null;
                    416:                    singleThreaded = false;
                    417:                    maximum = 1;
                    418:                 }
                    419:                 return servlet;
                    420:            }
                    421:            else return null;
1.59      ylafon    422:         }
                    423:         
                    424:         /**
                    425:          * blocking method for accessing a servlet instance from the pool
1.60      ylafon    426:          * @exception ServletException thrown if the pool is not properly
                    427:         * initialized
1.59      ylafon    428:          */
                    429:         protected synchronized Servlet takeServlet() throws ServletException {
1.60      ylafon    430:             if ((!inited)||(capacity < 1)||
                    431:                (pool == null)||(loadedClass == null)) {
                    432:                throw new ServletException("Accessing servlet without"+
                    433:                                           " initialization.");
1.59      ylafon    434:             }
                    435:             if (singleThreaded) {
1.60      ylafon    436:                try {
                    437:                    while (true) {
                    438:                        if (usage < capacity) {
                    439:                            return pool[usage++];
                    440:                        } else {
                    441:                            if (capacity < maximum) {
                    442:                                if (launchServlet(loadedClass)) {
                    443:                                    notifyAll();
                    444:                                    Thread.currentThread().yield(); 
                    445:                                     // give previous waiters a chance
                    446:                                } else {
                    447:                                    wait();
                    448:                                }
                    449:                            } else {
                    450:                                wait();
                    451:                            }
                    452:                        }
                    453:                    }
                    454:                }
                    455:                catch (InterruptedException ex) {
                    456:                    throw new ServletException("Waiting for a free servlet"+
                    457:                                               " instance interrupted.");
                    458:                }
                    459:             } else {
                    460:                // One instance only is used in non single thread case 
                    461:                // (cf. servlet api for details)
                    462:                usage++;
                    463:                return pool[0];
1.59      ylafon    464:             }
                    465:         }
                    466:         
                    467:         /**
                    468:          * method for releasing a servlet instance into the pool after work
1.60      ylafon    469:          * @param servlet the instance to be returned to the pool
                    470:          * @exception ServletException thrown if pool is not properly 
                    471:         * initialized
1.59      ylafon    472:          */
1.60      ylafon    473:         protected synchronized void releaseServlet(Servlet servlet) 
                    474:            throws ServletException 
                    475:        {
                    476:             if ((!inited)||(capacity < 1)||(pool == null)||
                    477:                   (loadedClass == null)) {
                    478:                throw new ServletException("Releasing servlet without"+
                    479:                                           " initialization.");
1.59      ylafon    480:             }
1.60      ylafon    481:             if (usage > 0) {
                    482:                if (singleThreaded) {
                    483:                    pool[--usage] = servlet;
                    484:                    notifyAll();
                    485:                } else {
                    486:                    // In this case the servlet instance is shared, i.e. 
                    487:                    // we have a counting semaphore only.
                    488:                    usage--;
                    489:                }
                    490:             } else {
                    491:                throw new ServletException("Incorrect servlet release"+
                    492:                                           " occurred.");
                    493:            }
1.59      ylafon    494:         }
                    495:         
                    496:         /**
                    497:          * method for referencing a servlet instance of the pool
1.60      ylafon    498:          * This method was added for backward compatibility in order to 
                    499:         * support the deprecated getServlet() method,
                    500:          * which is structurally not applicable to the pool mechanism, i.e. 
                    501:         * it always returns null in
                    502:          * case of the SingleThreadModel (in accordance with the current 
                    503:         * servlet spec)
1.59      ylafon    504:          * @return a servlet reference or null
                    505:          */
                    506:         protected synchronized Servlet getRepresentative() {
1.60      ylafon    507:             if ((!inited)||(capacity < 1)||(loadedClass == null)) {
                    508:                return null;
                    509:            } else {
                    510:                if (singleThreaded) {
                    511:                    // FIXME, here we could also return pool[0],
                    512:                    // which is defined but probably taken.
                    513:                    // However, using pool[0] in a normal manner might 
                    514:                    // cause strange behavior 
                    515:                    // due to its single threaded design aspect.
                    516:                    return null;
                    517:                } else {
                    518:                    return pool[0];
                    519:                }
1.59      ylafon    520:             }
                    521:         }
1.60      ylafon    522:     }    
1.59      ylafon    523:     
1.62      ylafon    524:     private class ServletRunner implements Runnable {
1.61      ylafon    525:        Servlet srServlet = null;
                    526:        JigsawHttpServletRequest srReq = null;
                    527:        JigsawHttpServletResponse srResp = null;
1.55      ylafon    528: 
                    529:        public void run() {
1.62      ylafon    530:            // name modified in order to keep track of who is running
                    531:            // which instance, tk, 21.10.2001
                    532:            Thread t = Thread.currentThread();
                    533:            String ts = t.getName();
                    534:            t.setName("ServletRunner<" + ts +":"+ srServlet.hashCode() + ">");
1.58      ylafon    535:            // synchronization object
                    536:            Object o = null;
1.55      ylafon    537:            try {
1.58      ylafon    538:                Reply reply = srResp.getReply();
                    539:                if (reply != null) {
                    540:                    o = reply.getState(JigsawHttpServletResponse.MONITOR);
                    541:                }
1.55      ylafon    542:                srServlet.service(srReq , srResp);
1.60      ylafon    543:                 try {
                    544:                    servletPool.releaseServlet(srServlet);
1.67    ! ylafon    545:                } finally {
1.60      ylafon    546:                    srServlet = null;
                    547:                 }
1.55      ylafon    548:                srResp.flushStream(true);
1.56      ylafon    549:            } catch (UnavailableException uex) {
                    550:                String message = null;
                    551:                srResp.setStatus(HTTP.SERVICE_UNAVAILABLE);
                    552:                if (uex.isPermanent()) {
                    553:                    message = "<h2>The servlet is permanently "+
                    554:                        "unavailable :</h2>"+
                    555:                        "Details: <b>"+uex.getMessage()+"</b>";
                    556:                    try {
                    557:                        srResp.sendError(HTTP.SERVICE_UNAVAILABLE, message);
                    558:                    } catch (IOException ioex) {
                    559:                        // not much to do now...
                    560:                    }
                    561:                } else {
                    562:                    int delay = uex.getUnavailableSeconds();
                    563:                    if (delay > 0) {
                    564:                        message = "<h2>The servlet is temporarily "+
                    565:                            "unavailable :</h2>"+
                    566:                            "Delay : "+delay+
                    567:                            " seconds<br><br>Details: <b>"+
                    568:                            uex.getMessage()+"</b>";
                    569:                        srResp.getReply().setRetryAfter(delay);
                    570:                        try {
                    571:                            srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
                    572:                        } catch (IOException ioex) {
                    573:                            // not much to do now...
                    574:                        }
                    575:                    } else {
                    576:                        message = "<h2>The servlet is temporarily "+
                    577:                            "unavailable :</h2>"+
                    578:                            "Details: <b>"+uex.getMessage()+
                    579:                            "</b>";
                    580:                        try {           
                    581:                            srResp.sendError(HTTP.SERVICE_UNAVAILABLE,message);
                    582:                        } catch (IOException ioex) {
                    583:                            // not much to do now...
                    584:                        }
                    585:                    }
                    586:                }
1.55      ylafon    587:            } catch (Exception ex) {
1.65      ylafon    588:                if (debug) {
                    589:                    ex.printStackTrace();
                    590:                }
1.63      ylafon    591:                if (srResp.isStreamObtained()) {
                    592:                    try {
1.64      ylafon    593:                        srResp.flushStream(false);
1.66      ylafon    594:                        OutputStream os = srResp.getRawOutputStream();
1.64      ylafon    595:                        if (os != null) {
1.66      ylafon    596:                            synchronized (os) {
                    597:                                PrintWriter pw = new PrintWriter(os);
                    598:                                ex.printStackTrace(pw);
                    599:                                pw.flush();
                    600:                            }
1.64      ylafon    601:                        }
1.63      ylafon    602:                        srResp.flushStream(true);
                    603:                    } catch (IOException ioex) {}
                    604:                } else {
                    605:                    try {
                    606:                        srResp.sendError(HTTP.INTERNAL_SERVER_ERROR,
                    607:                                         "Servlet has thrown exception:" + 
                    608:                                         ex.toString());
                    609:                    } catch (IOException ioex) {
                    610:                        // no stream to write on, fail silently
                    611:                    }
1.56      ylafon    612:                }
                    613:            } finally {
1.60      ylafon    614:                 if (srServlet != null) {
                    615:                    try {
                    616:                        servletPool.releaseServlet(srServlet);
                    617:                    }
                    618:                    catch (ServletException ex) {
                    619:                        // ignore
                    620:                    }
                    621:                 }
1.56      ylafon    622:                // release the monitor waiting for the end of the reply setup
1.58      ylafon    623:                if (o != null) {
                    624:                    synchronized (o) {
                    625:                        o.notifyAll();
1.56      ylafon    626:                    }
                    627:                }
1.62      ylafon    628:                // revert name to original value
                    629:                t.setName(ts);
1.64      ylafon    630:                srServlet = null;
                    631:                srReq = null;
                    632:                srResp = null;
1.55      ylafon    633:            }
                    634:        }
                    635: 
                    636:        ServletRunner(Servlet servlet,
                    637:                      JigsawHttpServletRequest request,
                    638:                      JigsawHttpServletResponse response) {
                    639:            srServlet = servlet;
                    640:            srReq = request;
                    641:            srResp = response;
1.62      ylafon    642:        }
1.55      ylafon    643:     }
                    644: 
1.20      bmahe     645:     protected void service(Request request, Reply reply)
                    646:        throws ServletException, IOException
1.60      ylafon    647:        {
                    648:            JigsawHttpServletResponse jRes = null;
                    649:            JigsawHttpServletRequest jReq = null;
                    650:            /* modified due to servlet pooling, tk, 20.10.2001
                    651:               if (servlet instanceof SingleThreadModel) {
                    652:               synchronized (this) {
                    653:               jRes = new JigsawHttpServletResponse(request, reply);
                    654:               jReq = new JigsawHttpServletRequest(servlet, 
                    655:               request, 
                    656:               jRes,
                    657:               getSessionContext());
                    658:               jRes.setServletRequest(jReq);
                    659:               try {
                    660:               connections++;
                    661:               // FIXME we should reuse a thread rather than
                    662:               // reallocating one for every hit
                    663:               ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
                    664:               reply.setState(RUNNER, runner);
                    665:               runner.start();
                    666:               } finally {
                    667:               connections--;
                    668:               }
                    669:               }
                    670:               } else {
                    671:            */
1.31      bmahe     672:            jRes = new JigsawHttpServletResponse(request, reply);
1.60      ylafon    673:             Servlet servlet = servletPool.takeServlet(); 
                    674:             // accessing a fresh or sharable instance, tk, 21.10.2001
1.66      ylafon    675:            // jReq = new JigsawHttpServletRequest(servlet,
                    676:            JigsawServletContext jco = 
                    677:                                    (JigsawServletContext) getServletContext();
1.31      bmahe     678:            jReq = new JigsawHttpServletRequest(servlet, 
1.66      ylafon    679:                                                jco,
1.31      bmahe     680:                                                request, 
                    681:                                                jRes,
                    682:                                                getSessionContext());
                    683:            jRes.setServletRequest(jReq);
1.60      ylafon    684:            // try {
                    685:            //       connections++;
                    686:            // FIXME we should reuse a thread rather than
                    687:            // reallocating one for every hit
                    688:            // reallocating one for every hit
                    689:            ServletRunner runner = new ServletRunner(servlet, jReq, jRes);
                    690:            reply.setState(RUNNER, runner);
1.62      ylafon    691:            threadCache.getThread(runner, true);
                    692: //         runner.start();
1.60      ylafon    693:            // } finally {
                    694:            //     connections--;
                    695:            // }
                    696:            /* } */
                    697:            timeoutManager.restart();
                    698:        }
1.20      bmahe     699: 
                    700:     /**
                    701:      * Get the class name of the wrapped servlet.
                    702:      * @return The class name for the servlet if attribute is defined. 
                    703:      * Otherwise the class name is deduced from the resource identifier.
                    704:      */
                    705: 
                    706:     public String getServletClass()
1.60      ylafon    707:        {
                    708:            String sclass =  getString(ATTR_SERVLET_CLASS, null);
                    709:            if (sclass == null) {
                    710:                String ident = getIdentifier();
                    711:                if (ident.endsWith(".class")) {
                    712:                    sclass = ident;
                    713:                }
                    714:            }
                    715:            return sclass;
1.20      bmahe     716:        }
                    717: 
                    718:     /**
                    719:      * Get the init parameters for our wrapped servlet.
                    720:      * @return An ArrayDictionary instance if the attribute is defined, 
                    721:      * <strong>false</strong> otherwise.
                    722:      */
                    723: 
                    724:     public ArrayDictionary getServletParameters() {
                    725:        return (ArrayDictionary) getValue(ATTR_PARAMETERS, null);
                    726:     }
                    727: 
1.26      bmahe     728:     protected void setValueOfSuperClass(int idx, Object value) {
                    729:        super.setValue(idx, value);
                    730:     }
                    731: 
1.20      bmahe     732:     /**
                    733:      * Catch assignements to the servlet class name attribute.
                    734:      * <p>When a change to that attribute is detected, the servlet is
                    735:      * automatically reinitialized.
                    736:      */
                    737: 
                    738:     public void setValue(int idx, Object value) {
                    739:        super.setValue(idx, value);
1.44      bmahe     740:        if (((idx == ATTR_SERVLET_CLASS) && (value != null)) ||
                    741:            (idx == ATTR_PARAMETERS)) {
1.36      bmahe     742:            try {
1.60      ylafon    743:                 synchronized(servletPool) { 
                    744:                     // synchronization added, tk, 20.10.2001
1.59      ylafon    745:                    inited = launchServlet();
                    746:                 }
1.36      bmahe     747:            } catch (Exception ex) {
                    748:                String msg = ("unable to set servlet class \""+
                    749:                              getServletClass()+
                    750:                              "\" : "+
                    751:                              ex.getMessage());
                    752:                getServer().errlog(msg);
                    753:            }
1.40      bmahe     754:        } if (idx == ATTR_SERVLET_TIMEOUT) {
                    755:            timeoutManager.restart();
1.36      bmahe     756:        }
1.20      bmahe     757:     }
                    758: 
                    759:     /**
                    760:      * Destroy the servlet we are wrapping.
                    761:      */
1.60      ylafon    762:     protected void destroyServlet() {
1.59      ylafon    763:        // if ((servlet != null) && (connections < 1)) {
1.60      ylafon    764:        synchronized(servletPool) {
                    765:            Servlet servlet = servletPool.remove();
                    766:            while (servlet != null) {
                    767:                servlet.destroy();
                    768:                // servlet = null;
                    769:                servlet = servletPool.remove();
                    770:            }
                    771:            inited = (servletPool.getLoadedClass() != null);
1.20      bmahe     772:        }
1.60      ylafon    773:        // }
1.20      bmahe     774:     }
                    775: 
                    776:     /**
                    777:      * Get the servlet we are wrapping.
                    778:      * @return A servlet instance, if the servlet is alredy running, 
                    779:      * <strong>null</strong> otherwise.
                    780:      */
1.60      ylafon    781:     public Servlet getServlet() {
1.36      bmahe     782:        try {
                    783:            checkServlet();
                    784:        } catch (Exception ex) {
                    785:            if (debug)
                    786:                ex.printStackTrace();
                    787:        }
1.59      ylafon    788:        return servletPool.getRepresentative();
1.20      bmahe     789:     }
1.59      ylafon    790:     
1.20      bmahe     791:     /**
                    792:      * Initialize our servlet from the given (loaded) class.
                    793:      * @param cls The servlet loaded main class.
                    794:      * @return A boolean, <strong>true</strong> if servlet was successfully
                    795:      * initialised, <strong>false</strong> otherwise.
1.37      bmahe     796:      * @exception ServletException if servlet can't be initialized.
1.20      bmahe     797:      */
                    798: 
1.36      bmahe     799:     protected boolean launchServlet(Class cls) 
                    800:        throws ServletException
1.60      ylafon    801:        {
                    802:            if (debug) {
                    803:                System.out.println("launching Servlet: "+getServletName());
                    804:            }
                    805:            Servlet servlet = null;
                    806:            try {
                    807:                servlet = (Servlet) cls.newInstance();
                    808:                servlet.init((ServletConfig) this);
                    809:                timeoutManager.restart();
                    810:            } catch (IllegalAccessException ex) {
                    811:                String msg = ("Illegal access during servlet instantiation, "+
                    812:                              ex.getClass().getName()+": "+
                    813:                              ex.getMessage());
                    814:                if ( debug ) {
                    815:                    ex.printStackTrace();
                    816:                }
                    817:                getServer().errlog(this, msg);
                    818:                return false;
                    819:            } catch (InstantiationException iex) {
                    820:                String msg = ("unable to instantiate servlet, "+
                    821:                              iex.getClass().getName()+": "+
                    822:                              iex.getMessage());
                    823:                if ( debug ) {
                    824:                    iex.printStackTrace();
                    825:                }
                    826:                getServer().errlog(this, msg);
                    827:                return false;
1.67    ! ylafon    828:            } catch (Exception oex) {
        !           829:                String msg = ("Error while loading servlet");
        !           830:                getServer().errlog(this, msg);
        !           831:                if ( debug ) {
        !           832:                    oex.printStackTrace();
        !           833:                }
1.60      ylafon    834:            }
                    835:            // modified for servlet pool and executed in a context 
                    836:            // synchronized on the servlet pool, tk, 20.10.2001
                    837:            if (servlet != null) {
                    838:                servletPool.add(servlet);
                    839:                return true;
                    840:            } else {
                    841:                return false;
                    842:            }
1.50      bmahe     843:        }
1.32      bmahe     844: 
                    845:     /**
                    846:      * Check if the Servletclass wrapped is a Servlet class without
1.36      bmahe     847:      * initializing it. (not the same than checkServlet).
1.33      bmahe     848:      * used by the ServletIndexer.
                    849:      * @see org.w3c.jigsaw.servlet.ServletIndexer
1.32      bmahe     850:      * @return A boolean.
                    851:      */
                    852:     protected boolean isWrappingAServlet() {
                    853:        String clsname = getServletClass();
1.60      ylafon    854:        if ( clsname == null ) {
1.32      bmahe     855:            return false;
1.60      ylafon    856:        }
1.32      bmahe     857:        Class c = null;
                    858:        try {
1.34      bmahe     859:            c = getLocalServletLoader().loadClass(clsname, true);
1.32      bmahe     860:            Object o = c.newInstance();
                    861:            return (o instanceof Servlet);
                    862:        } catch (Exception ex) {
                    863:            return false;
                    864:        }
1.20      bmahe     865:     }
                    866: 
                    867:     /**
                    868:      * Launch the servlet we are wrapping.
                    869:      * <p>This method either succeed, or the wrapper resource itself will fail
                    870:      * to initialize, acting as transparently as possible (in some sense).
                    871:      * @return A boolean, <strong>true</strong> if servlet launched.
1.37      bmahe     872:      * @exception ClassNotFoundException if servlet class can't be found.
                    873:      * @exception ServletException if servlet can't be initialized.
1.20      bmahe     874:      */
                    875: 
1.36      bmahe     876:     protected boolean launchServlet() 
                    877:        throws ClassNotFoundException, ServletException
1.60      ylafon    878:        {
                    879:            // Get and check the servlet class:
                    880:            // if ( servlet != null )
1.20      bmahe     881:            destroyServlet();
1.60      ylafon    882:            if (inited) { 
                    883:                String msg = "relaunching servlet failed due to incomplete \""
                    884:                    + getServletClass() + "\" cleanup.";
                    885:                getServer().errlog(this, msg); 
                    886:                return false;
                    887:            } else {
                    888:                // Load appropriate servlet class:
                    889:                String clsname = getServletClass();
                    890:                if ( clsname == null ) {
                    891:                    getServer().errlog(this, "no servlet class attribute"+
                    892:                                       " defined.");
                    893:                    return false;
                    894:                } else {
                    895:                    Class c = null;
                    896:                    try {
                    897:                        if (getLocalServletLoader().classChanged(clsname)) {
                    898:                            createNewLocalServletLoader(true);
                    899:                            invalidateAllSession();
                    900:                        }
                    901:                        c = getLocalServletLoader().loadClass(clsname, true);
                    902:                    } catch (ClassNotFoundException ex) {
                    903:                        String msg = ("unable to find servlet class \""+
                    904:                                      getServletClass()+"\"");
                    905:                        getServer().errlog(msg);
                    906:                        // re throw the exception
                    907:                        throw ex;
                    908:                    }
                    909:                    return (c != null) ? launchServlet(c) : false;
                    910:                }
1.20      bmahe     911:            }
                    912:        }
1.59      ylafon    913:     
1.60      ylafon    914:     public boolean acceptUnload() {
1.59      ylafon    915:        // return (servlet == null);
1.60      ylafon    916:         return (!inited);
1.40      bmahe     917:     }
1.59      ylafon    918:     
1.20      bmahe     919:     public void notifyUnload() {
1.60      ylafon    920:        if (timeoutManager != null) {
1.42      bmahe     921:            timeoutManager.stop();
1.60      ylafon    922:        }
1.20      bmahe     923:        destroyServlet();
1.26      bmahe     924:     }
                    925: 
                    926:     /** 
                    927:      * Get or create a suitable LocalServletLoader instance to load 
                    928:      * that servlet.
                    929:      * @return A LocalServletLoader instance.
                    930:      */
1.60      ylafon    931:     // singleton synchronized already performed at the servlet context.
                    932:     // protected synchronized AutoReloadServletLoader getLocalServletLoader() {
                    933:     protected AutoReloadServletLoader getLocalServletLoader() {
1.34      bmahe     934:        JigsawServletContext ctxt = (JigsawServletContext) getServletContext();
                    935:        return ctxt.getLocalServletLoader();
1.26      bmahe     936:     }
                    937: 
1.60      ylafon    938:     protected AutoReloadServletLoader createNewLocalServletLoader(boolean 
                    939:                                                                  keepold) 
1.26      bmahe     940:     {
1.60      ylafon    941:        JigsawServletContext ctxt = (JigsawServletContext) 
                    942:                                                      getServletContext();
1.34      bmahe     943:        return ctxt.createNewLocalServletLoader(keepold);
1.20      bmahe     944:     }
                    945: 
                    946:     /**
1.48      bmahe     947:      * Returns the name of this servlet instance.
                    948:      * The name may be provided via server administration, assigned in the 
                    949:      * web application deployment descriptor, or for an unregistered (and thus
                    950:      * unnamed) servlet instance it will be the servlet's class name.
                    951:      * @return the name of the servlet instance
                    952:      */
                    953:     public String getServletName() {
                    954:        return getIdentifier();
                    955:     }
                    956: 
                    957:     /**
1.20      bmahe     958:      * Initialize this servlet wrapper resource.
                    959:      * After the wrapper itself is inited, it performs the servlet 
                    960:      * initialzation.
                    961:      * @param values The default attribute values.
                    962:      */
                    963:     public void initialize(Object values[]) {
                    964:        super.initialize(values);
1.60      ylafon    965:        // connections = 0;
1.41      bmahe     966:        
                    967:        if (getServletContext() != null) {
                    968:            timeoutManager = new TimeoutManager((httpd)getServer());
                    969:            timeoutManager.start();
                    970:        }
1.40      bmahe     971: 
1.20      bmahe     972:        try {
                    973:            registerFrameIfNone("org.w3c.jigsaw.servlet.ServletWrapperFrame",
                    974:                                "servlet-wrapper-frame");
                    975:        } catch (Exception ex) {
                    976:            ex.printStackTrace();
                    977:        }
1.18      bmahe     978:     }
1.1       abaird    979: }

Webmaster