1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.servlet;
20
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.Collection;
25import java.util.Collections;
26import java.util.HashMap;
27import java.util.HashSet;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import java.util.Stack;
32
33import javax.servlet.MultipartConfigElement;
34import javax.servlet.Servlet;
35import javax.servlet.ServletConfig;
36import javax.servlet.ServletException;
37import javax.servlet.ServletRegistration;
38import javax.servlet.ServletContext;
39import javax.servlet.ServletRequest;
40import javax.servlet.ServletResponse;
41import javax.servlet.ServletSecurityElement;
42import javax.servlet.SingleThreadModel;
43import javax.servlet.UnavailableException;
44
45import org.eclipse.jetty.security.IdentityService;
46import org.eclipse.jetty.security.RunAsToken;
47import org.eclipse.jetty.server.Request;
48import org.eclipse.jetty.server.UserIdentity;
49import org.eclipse.jetty.server.handler.ContextHandler;
50import org.eclipse.jetty.util.Loader;
51import org.eclipse.jetty.util.log.Log;
52import org.eclipse.jetty.util.log.Logger;
53
54
55
56
57/* --------------------------------------------------------------------- */
58/** Servlet Instance and Context Holder.
59 * Holds the name, params and some state of a javax.servlet.Servlet
60 * instance. It implements the ServletConfig interface.
61 * This class will organise the loading of the servlet when needed or
62 * requested.
63 *
64 *
65 */
66public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable
67{
68    private static final Logger LOG = Log.getLogger(ServletHolder.class);
69
70    /* ---------------------------------------------------------------- */
71    private int _initOrder;
72    private boolean _initOnStartup=false;
73    private Map<String, String> _roleMap;
74    private String _forcedPath;
75    private String _runAsRole;
76    private RunAsToken _runAsToken;
77    private IdentityService _identityService;
78    private ServletRegistration.Dynamic _registration;
79
80
81    private transient Servlet _servlet;
82    private transient Config _config;
83    private transient long _unavailable;
84    private transient boolean _enabled = true;
85    private transient UnavailableException _unavailableEx;
86    public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
87
88    /* ---------------------------------------------------------------- */
89    /** Constructor .
90     */
91    public ServletHolder()
92    {
93        this(Source.EMBEDDED);
94    }
95
96    /* ---------------------------------------------------------------- */
97    /** Constructor .
98     */
99    public ServletHolder(Holder.Source creator)
100    {
101        super(creator);
102    }
103
104    /* ---------------------------------------------------------------- */
105    /** Constructor for existing servlet.
106     */
107    public ServletHolder(Servlet servlet)
108    {
109        this(Source.EMBEDDED);
110        setServlet(servlet);
111    }
112
113    /* ---------------------------------------------------------------- */
114    /** Constructor for servlet class.
115     */
116    public ServletHolder(String name, Class<? extends Servlet> servlet)
117    {
118        this(Source.EMBEDDED);
119        setName(name);
120        setHeldClass(servlet);
121    }
122
123    /* ---------------------------------------------------------------- */
124    /** Constructor for servlet class.
125     */
126    public ServletHolder(String name, Servlet servlet)
127    {
128        this(Source.EMBEDDED);
129        setName(name);
130        setServlet(servlet);
131    }
132
133    /* ---------------------------------------------------------------- */
134    /** Constructor for servlet class.
135     */
136    public ServletHolder(Class<? extends Servlet> servlet)
137    {
138        this(Source.EMBEDDED);
139        setHeldClass(servlet);
140    }
141
142    /* ---------------------------------------------------------------- */
143    /**
144     * @return The unavailable exception or null if not unavailable
145     */
146    public UnavailableException getUnavailableException()
147    {
148        return _unavailableEx;
149    }
150
151    /* ------------------------------------------------------------ */
152    public synchronized void setServlet(Servlet servlet)
153    {
154        if (servlet==null || servlet instanceof SingleThreadModel)
155            throw new IllegalArgumentException();
156
157        _extInstance=true;
158        _servlet=servlet;
159        setHeldClass(servlet.getClass());
160        if (getName()==null)
161            setName(servlet.getClass().getName()+"-"+super.hashCode());
162    }
163
164    /* ------------------------------------------------------------ */
165    public int getInitOrder()
166    {
167        return _initOrder;
168    }
169
170    /* ------------------------------------------------------------ */
171    /** Set the initialize order.
172     * Holders with order<0, are initialized on use. Those with
173     * order>=0 are initialized in increasing order when the handler
174     * is started.
175     */
176    public void setInitOrder(int order)
177    {
178        _initOnStartup=true;
179        _initOrder = order;
180    }
181
182    public boolean isSetInitOrder()
183    {
184        return _initOnStartup;
185    }
186
187    /* ------------------------------------------------------------ */
188    /** Comparitor by init order.
189     */
190    public int compareTo(Object o)
191    {
192        if (o instanceof ServletHolder)
193        {
194            ServletHolder sh= (ServletHolder)o;
195            if (sh==this)
196                return 0;
197            if (sh._initOrder<_initOrder)
198                return 1;
199            if (sh._initOrder>_initOrder)
200                return -1;
201
202            int c=(_className!=null && sh._className!=null)?_className.compareTo(sh._className):0;
203            if (c==0)
204                c=_name.compareTo(sh._name);
205            return c;
206        }
207        return 1;
208    }
209
210    /* ------------------------------------------------------------ */
211    public boolean equals(Object o)
212    {
213        return compareTo(o)==0;
214    }
215
216    /* ------------------------------------------------------------ */
217    public int hashCode()
218    {
219        return _name==null?System.identityHashCode(this):_name.hashCode();
220    }
221
222    /* ------------------------------------------------------------ */
223    /** Link a user role.
224     * Translate the role name used by a servlet, to the link name
225     * used by the container.
226     * @param name The role name as used by the servlet
227     * @param link The role name as used by the container.
228     */
229    public synchronized void setUserRoleLink(String name,String link)
230    {
231        if (_roleMap==null)
232            _roleMap=new HashMap<String, String>();
233        _roleMap.put(name,link);
234    }
235
236    /* ------------------------------------------------------------ */
237    /** get a user role link.
238     * @param name The name of the role
239     * @return The name as translated by the link. If no link exists,
240     * the name is returned.
241     */
242    public String getUserRoleLink(String name)
243    {
244        if (_roleMap==null)
245            return name;
246        String link= _roleMap.get(name);
247        return (link==null)?name:link;
248    }
249
250    /* ------------------------------------------------------------ */
251    public Map<String, String> getRoleMap()
252    {
253        return _roleMap == null? NO_MAPPED_ROLES : _roleMap;
254    }
255
256    /* ------------------------------------------------------------ */
257    /**
258     * @return Returns the forcedPath.
259     */
260    public String getForcedPath()
261    {
262        return _forcedPath;
263    }
264
265    /* ------------------------------------------------------------ */
266    /**
267     * @param forcedPath The forcedPath to set.
268     */
269    public void setForcedPath(String forcedPath)
270    {
271        _forcedPath = forcedPath;
272    }
273
274    public boolean isEnabled()
275    {
276        return _enabled;
277    }
278
279
280    public void setEnabled(boolean enabled)
281    {
282        _enabled = enabled;
283    }
284
285
286    /* ------------------------------------------------------------ */
287    public void doStart()
288        throws Exception
289    {
290        _unavailable=0;
291        if (!_enabled)
292            return;
293
294
295        //check servlet has a class (ie is not a preliminary registration). If preliminary, fail startup.
296        try
297        {
298            super.doStart();
299        }
300        catch (UnavailableException ue)
301        {
302            makeUnavailable(ue);
303            if (_servletHandler.isStartWithUnavailable())
304            {
305                LOG.ignore(ue);
306                return;
307            }
308            else
309                throw ue;
310        }
311
312
313        //servlet is not an instance of javax.servlet.Servlet
314        try
315        {
316            checkServletType();
317        }
318        catch (UnavailableException ue)
319        {
320            makeUnavailable(ue);
321            if (_servletHandler.isStartWithUnavailable())
322            {
323                LOG.ignore(ue);
324                return;
325            }
326            else
327                throw ue;
328        }
329
330
331        _identityService = _servletHandler.getIdentityService();
332        if (_identityService!=null && _runAsRole!=null)
333            _runAsToken=_identityService.newRunAsToken(_runAsRole);
334
335        _config=new Config();
336
337        if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
338            _servlet = new SingleThreadedWrapper();
339
340        if (_extInstance || _initOnStartup)
341        {
342            try
343            {
344                initServlet();
345            }
346            catch(Exception e)
347            {
348                if (_servletHandler.isStartWithUnavailable())
349                    LOG.ignore(e);
350                else
351                    throw e;
352            }
353        }
354    }
355
356    /* ------------------------------------------------------------ */
357    public void doStop()
358        throws Exception
359    {
360        Object old_run_as = null;
361        if (_servlet!=null)
362        {
363            try
364            {
365                if (_identityService!=null)
366                    old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
367
368                destroyInstance(_servlet);
369            }
370            catch (Exception e)
371            {
372                LOG.warn(e);
373            }
374            finally
375            {
376                if (_identityService!=null)
377                    _identityService.unsetRunAs(old_run_as);
378            }
379        }
380
381        if (!_extInstance)
382            _servlet=null;
383
384        _config=null;
385    }
386
387    /* ------------------------------------------------------------ */
388    public void destroyInstance (Object o)
389    throws Exception
390    {
391        if (o==null)
392            return;
393        Servlet servlet =  ((Servlet)o);
394        getServletHandler().destroyServlet(servlet);
395        servlet.destroy();
396    }
397
398    /* ------------------------------------------------------------ */
399    /** Get the servlet.
400     * @return The servlet
401     */
402    public synchronized Servlet getServlet()
403        throws ServletException
404    {
405        // Handle previous unavailability
406        if (_unavailable!=0)
407        {
408            if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
409                throw _unavailableEx;
410            _unavailable=0;
411            _unavailableEx=null;
412        }
413
414        if (_servlet==null)
415            initServlet();
416        return _servlet;
417    }
418
419    /* ------------------------------------------------------------ */
420    /** Get the servlet instance (no initialization done).
421     * @return The servlet or null
422     */
423    public Servlet getServletInstance()
424    {
425        return _servlet;
426    }
427
428    /* ------------------------------------------------------------ */
429    /**
430     * Check to ensure class of servlet is acceptable.
431     * @throws UnavailableException
432     */
433    public void checkServletType ()
434        throws UnavailableException
435    {
436        if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
437        {
438            throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
439        }
440    }
441
442    /* ------------------------------------------------------------ */
443    /**
444     * @return true if the holder is started and is not unavailable
445     */
446    public boolean isAvailable()
447    {
448        if (isStarted()&& _unavailable==0)
449            return true;
450        try
451        {
452            getServlet();
453        }
454        catch(Exception e)
455        {
456            LOG.ignore(e);
457        }
458
459        return isStarted()&& _unavailable==0;
460    }
461
462    /* ------------------------------------------------------------ */
463    private void makeUnavailable(UnavailableException e)
464    {
465        if (_unavailableEx==e && _unavailable!=0)
466            return;
467
468        _servletHandler.getServletContext().log("unavailable",e);
469
470        _unavailableEx=e;
471        _unavailable=-1;
472        if (e.isPermanent())
473            _unavailable=-1;
474        else
475        {
476            if (_unavailableEx.getUnavailableSeconds()>0)
477                _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
478            else
479                _unavailable=System.currentTimeMillis()+5000; // TODO configure
480        }
481    }
482
483    /* ------------------------------------------------------------ */
484
485    private void makeUnavailable(final Throwable e)
486    {
487        if (e instanceof UnavailableException)
488            makeUnavailable((UnavailableException)e);
489        else
490        {
491            ServletContext ctx = _servletHandler.getServletContext();
492            if (ctx==null)
493                LOG.info("unavailable",e);
494            else
495                ctx.log("unavailable",e);
496            _unavailableEx=new UnavailableException(String.valueOf(e),-1)
497            {
498                {
499                    initCause(e);
500                }
501            };
502            _unavailable=-1;
503        }
504    }
505
506    /* ------------------------------------------------------------ */
507    private void initServlet()
508    	throws ServletException
509    {
510        Object old_run_as = null;
511        try
512        {
513            if (_servlet==null)
514                _servlet=newInstance();
515            if (_config==null)
516                _config=new Config();
517
518            // Handle run as
519            if (_identityService!=null)
520            {
521                old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
522            }
523
524            // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
525            if (isJspServlet())
526            {
527                initJspServlet();
528            }
529
530            initMultiPart();
531
532            _servlet.init(_config);
533        }
534        catch (UnavailableException e)
535        {
536            makeUnavailable(e);
537            _servlet=null;
538            _config=null;
539            throw e;
540        }
541        catch (ServletException e)
542        {
543            makeUnavailable(e.getCause()==null?e:e.getCause());
544            _servlet=null;
545            _config=null;
546            throw e;
547        }
548        catch (Exception e)
549        {
550            makeUnavailable(e);
551            _servlet=null;
552            _config=null;
553            throw new ServletException(this.toString(),e);
554        }
555        finally
556        {
557            // pop run-as role
558            if (_identityService!=null)
559                _identityService.unsetRunAs(old_run_as);
560        }
561    }
562
563
564    /* ------------------------------------------------------------ */
565    /**
566     * @throws Exception
567     */
568    protected void initJspServlet () throws Exception
569    {
570        ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
571
572        /* Set the webapp's classpath for Jasper */
573        ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
574
575        /* Set the system classpath for Jasper */
576        setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent()));
577
578        /* Set up other classpath attribute */
579        if ("?".equals(getInitParameter("classpath")))
580        {
581            String classpath = ch.getClassPath();
582            LOG.debug("classpath=" + classpath);
583            if (classpath != null)
584                setInitParameter("classpath", classpath);
585        }
586    }
587
588    /* ------------------------------------------------------------ */
589    /**
590     * Register a ServletRequestListener that will ensure tmp multipart
591     * files are deleted when the request goes out of scope.
592     *
593     * @throws Exception
594     */
595    protected void initMultiPart () throws Exception
596    {
597        //if this servlet can handle multipart requests, ensure tmp files will be
598        //cleaned up correctly
599        if (((Registration)getRegistration()).getMultipartConfig() != null)
600        {
601            //Register a listener to delete tmp files that are created as a result of this
602            //servlet calling Request.getPart() or Request.getParts()
603            ContextHandler ch = ((ContextHandler.Context)getServletHandler().getServletContext()).getContextHandler();
604            ch.addEventListener(new Request.MultiPartCleanerListener());
605        }
606    }
607
608    /* ------------------------------------------------------------ */
609    /**
610     * @see org.eclipse.jetty.server.UserIdentity.Scope#getContextPath()
611     */
612    public String getContextPath()
613    {
614        return _config.getServletContext().getContextPath();
615    }
616
617    /* ------------------------------------------------------------ */
618    /**
619     * @see org.eclipse.jetty.server.UserIdentity.Scope#getRoleRefMap()
620     */
621    public Map<String, String> getRoleRefMap()
622    {
623        return _roleMap;
624    }
625
626    /* ------------------------------------------------------------ */
627    public String getRunAsRole()
628    {
629        return _runAsRole;
630    }
631
632    /* ------------------------------------------------------------ */
633    public void setRunAsRole(String role)
634    {
635        _runAsRole = role;
636    }
637
638    /* ------------------------------------------------------------ */
639    /** Service a request with this servlet.
640     */
641    public void handle(Request baseRequest,
642                       ServletRequest request,
643                       ServletResponse response)
644        throws ServletException,
645               UnavailableException,
646               IOException
647    {
648        if (_class==null)
649            throw new UnavailableException("Servlet Not Initialized");
650
651        Servlet servlet=_servlet;
652        synchronized(this)
653        {
654            if (!isStarted())
655                throw new UnavailableException("Servlet not initialized", -1);
656            if (_unavailable!=0 || !_initOnStartup)
657                servlet=getServlet();
658            if (servlet==null)
659                throw new UnavailableException("Could not instantiate "+_class);
660        }
661
662        // Service the request
663        boolean servlet_error=true;
664        Object old_run_as = null;
665        boolean suspendable = baseRequest.isAsyncSupported();
666        try
667        {
668            // Handle aliased path
669            if (_forcedPath!=null)
670                // TODO complain about poor naming to the Jasper folks
671                request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
672
673            // Handle run as
674            if (_identityService!=null)
675                old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
676
677            if (!isAsyncSupported())
678                baseRequest.setAsyncSupported(false);
679
680            MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
681            if (mpce != null)
682                request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
683
684            servlet.service(request,response);
685            servlet_error=false;
686        }
687        catch(UnavailableException e)
688        {
689            makeUnavailable(e);
690            throw _unavailableEx;
691        }
692        finally
693        {
694            baseRequest.setAsyncSupported(suspendable);
695
696            // pop run-as role
697            if (_identityService!=null)
698                _identityService.unsetRunAs(old_run_as);
699
700            // Handle error params.
701            if (servlet_error)
702                request.setAttribute("javax.servlet.error.servlet_name",getName());
703        }
704    }
705
706
707    /* ------------------------------------------------------------ */
708    private boolean isJspServlet ()
709    {
710        if (_servlet == null)
711            return false;
712
713        Class c = _servlet.getClass();
714
715        boolean result = false;
716        while (c != null && !result)
717        {
718            result = isJspServlet(c.getName());
719            c = c.getSuperclass();
720        }
721
722        return result;
723    }
724
725
726    /* ------------------------------------------------------------ */
727    private boolean isJspServlet (String classname)
728    {
729        if (classname == null)
730            return false;
731        return ("org.apache.jasper.servlet.JspServlet".equals(classname));
732    }
733
734
735    /* ------------------------------------------------------------ */
736    /* ------------------------------------------------------------ */
737    /* ------------------------------------------------------------ */
738    protected class Config extends HolderConfig implements ServletConfig
739    {
740        /* -------------------------------------------------------- */
741        public String getServletName()
742        {
743            return getName();
744        }
745
746    }
747
748    /* -------------------------------------------------------- */
749    /* -------------------------------------------------------- */
750    /* -------------------------------------------------------- */
751    public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
752    {
753        protected MultipartConfigElement _multipartConfig;
754
755        public Set<String> addMapping(String... urlPatterns)
756        {
757            illegalStateIfContextStarted();
758            Set<String> clash=null;
759            for (String pattern : urlPatterns)
760            {
761                ServletMapping mapping = _servletHandler.getServletMapping(pattern);
762                if (mapping!=null)
763                {
764                    //if the servlet mapping was from a default descriptor, then allow it to be overridden
765                    if (!mapping.isDefault())
766                    {
767                        if (clash==null)
768                            clash=new HashSet<String>();
769                        clash.add(pattern);
770                    }
771                }
772            }
773
774            //if there were any clashes amongst the urls, return them
775            if (clash!=null)
776                return clash;
777
778            //otherwise apply all of them
779            ServletMapping mapping = new ServletMapping();
780            mapping.setServletName(ServletHolder.this.getName());
781            mapping.setPathSpecs(urlPatterns);
782            _servletHandler.addServletMapping(mapping);
783
784            return Collections.emptySet();
785        }
786
787        public Collection<String> getMappings()
788        {
789            ServletMapping[] mappings =_servletHandler.getServletMappings();
790            List<String> patterns=new ArrayList<String>();
791            if (mappings!=null)
792            {
793                for (ServletMapping mapping : mappings)
794                {
795                    if (!mapping.getServletName().equals(getName()))
796                        continue;
797                    String[] specs=mapping.getPathSpecs();
798                    if (specs!=null && specs.length>0)
799                        patterns.addAll(Arrays.asList(specs));
800                }
801            }
802            return patterns;
803        }
804
805        @Override
806        public String getRunAsRole()
807        {
808            return _runAsRole;
809        }
810
811        @Override
812        public void setLoadOnStartup(int loadOnStartup)
813        {
814            illegalStateIfContextStarted();
815            ServletHolder.this.setInitOrder(loadOnStartup);
816        }
817
818        public int getInitOrder()
819        {
820            return ServletHolder.this.getInitOrder();
821        }
822
823        @Override
824        public void setMultipartConfig(MultipartConfigElement element)
825        {
826            _multipartConfig = element;
827        }
828
829        public MultipartConfigElement getMultipartConfig()
830        {
831            return _multipartConfig;
832        }
833
834        @Override
835        public void setRunAsRole(String role)
836        {
837            _runAsRole = role;
838        }
839
840        @Override
841        public Set<String> setServletSecurity(ServletSecurityElement securityElement)
842        {
843            return _servletHandler.setServletSecurity(this, securityElement);
844        }
845    }
846
847    public ServletRegistration.Dynamic getRegistration()
848    {
849        if (_registration == null)
850            _registration = new Registration();
851        return _registration;
852    }
853
854    /* -------------------------------------------------------- */
855    /* -------------------------------------------------------- */
856    /* -------------------------------------------------------- */
857    private class SingleThreadedWrapper implements Servlet
858    {
859        Stack<Servlet> _stack=new Stack<Servlet>();
860
861        public void destroy()
862        {
863            synchronized(this)
864            {
865                while(_stack.size()>0)
866                    try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
867            }
868        }
869
870        public ServletConfig getServletConfig()
871        {
872            return _config;
873        }
874
875        public String getServletInfo()
876        {
877            return null;
878        }
879
880        public void init(ServletConfig config) throws ServletException
881        {
882            synchronized(this)
883            {
884                if(_stack.size()==0)
885                {
886                    try
887                    {
888                        Servlet s = newInstance();
889                        s.init(config);
890                        _stack.push(s);
891                    }
892                    catch (ServletException e)
893                    {
894                        throw e;
895                    }
896                    catch (Exception e)
897                    {
898                        throw new ServletException(e);
899                    }
900                }
901            }
902        }
903
904        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
905        {
906            Servlet s;
907            synchronized(this)
908            {
909                if(_stack.size()>0)
910                    s=(Servlet)_stack.pop();
911                else
912                {
913                    try
914                    {
915                        s = newInstance();
916                        s.init(_config);
917                    }
918                    catch (ServletException e)
919                    {
920                        throw e;
921                    }
922                    catch (Exception e)
923                    {
924                        throw new ServletException(e);
925                    }
926                }
927            }
928
929            try
930            {
931                s.service(req,res);
932            }
933            finally
934            {
935                synchronized(this)
936                {
937                    _stack.push(s);
938                }
939            }
940        }
941    }
942
943    /* ------------------------------------------------------------ */
944    /**
945     * @return the newly created Servlet instance
946     * @throws ServletException
947     * @throws IllegalAccessException
948     * @throws InstantiationException
949     */
950    protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
951    {
952        try
953        {
954            ServletContext ctx = getServletHandler().getServletContext();
955            if (ctx==null)
956                return getHeldClass().newInstance();
957            return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
958        }
959        catch (ServletException se)
960        {
961            Throwable cause = se.getRootCause();
962            if (cause instanceof InstantiationException)
963                throw (InstantiationException)cause;
964            if (cause instanceof IllegalAccessException)
965                throw (IllegalAccessException)cause;
966            throw se;
967        }
968    }
969}
970