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

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

Webmaster