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

1.1       abaird      1: // ServletWrapper.java
1.61    ! ylafon      2: // $Id: ServletWrapper.java,v 1.60 2001/11/12 14:03:35 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;
                     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 {
1.61    ! ylafon    512:        Servlet srServlet = null;
        !           513:        JigsawHttpServletRequest srReq = null;
        !           514:        JigsawHttpServletResponse srResp = null;
1.55      ylafon    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