1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.core;
18
19 import java.io.PrintStream;
20 import java.lang.reflect.Method;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Enumeration;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.Stack;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.concurrent.locks.ReentrantReadWriteLock;
30
31 import javax.management.ListenerNotFoundException;
32 import javax.management.MBeanNotificationInfo;
33 import javax.management.Notification;
34 import javax.management.NotificationBroadcasterSupport;
35 import javax.management.NotificationEmitter;
36 import javax.management.NotificationFilter;
37 import javax.management.NotificationListener;
38 import javax.management.ObjectName;
39 import javax.servlet.MultipartConfigElement;
40 import javax.servlet.Servlet;
41 import javax.servlet.ServletConfig;
42 import javax.servlet.ServletContext;
43 import javax.servlet.ServletException;
44 import javax.servlet.SingleThreadModel;
45 import javax.servlet.UnavailableException;
46 import javax.servlet.annotation.MultipartConfig;
47
48 import org.apache.catalina.Container;
49 import org.apache.catalina.ContainerServlet;
50 import org.apache.catalina.Context;
51 import org.apache.catalina.Globals;
52 import org.apache.catalina.LifecycleException;
53 import org.apache.catalina.LifecycleState;
54 import org.apache.catalina.Wrapper;
55 import org.apache.catalina.security.SecurityUtil;
56 import org.apache.juli.logging.Log;
57 import org.apache.juli.logging.LogFactory;
58 import org.apache.tomcat.InstanceManager;
59 import org.apache.tomcat.PeriodicEventListener;
60 import org.apache.tomcat.util.ExceptionUtils;
61 import org.apache.tomcat.util.log.SystemLogHandler;
62 import org.apache.tomcat.util.modeler.Registry;
63 import org.apache.tomcat.util.modeler.Util;
64
65 /**
66  * Standard implementation of the <b>Wrapper</b> interface that represents
67  * an individual servlet definition.  No child Containers are allowed, and
68  * the parent Container must be a Context.
69  *
70  * @author Craig R. McClanahan
71  * @author Remy Maucherat
72  */

73 @SuppressWarnings("deprecation"// SingleThreadModel
74 public class StandardWrapper extends ContainerBase
75     implements ServletConfig, Wrapper, NotificationEmitter {
76
77     private final Log log = LogFactory.getLog(StandardWrapper.class); // must not be static
78
79     protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
80                                                     "GET""HEAD""POST" };
81
82     // ----------------------------------------------------------- Constructors
83
84
85     /**
86      * Create a new StandardWrapper component with the default basic Valve.
87      */

88     public StandardWrapper() {
89
90         super();
91         swValve=new StandardWrapperValve();
92         pipeline.setBasic(swValve);
93         broadcaster = new NotificationBroadcasterSupport();
94
95     }
96
97
98     // ----------------------------------------------------- Instance Variables
99
100
101     /**
102      * The date and time at which this servlet will become available (in
103      * milliseconds since the epoch), or zero if the servlet is available.
104      * If this value equals Long.MAX_VALUE, the unavailability of this
105      * servlet is considered permanent.
106      */

107     protected long available = 0L;
108
109     /**
110      * The broadcaster that sends j2ee notifications.
111      */

112     protected final NotificationBroadcasterSupport broadcaster;
113
114     /**
115      * The count of allocations that are currently active (even if they
116      * are for the same instance, as will be true on a non-STM servlet).
117      */

118     protected final AtomicInteger countAllocated = new AtomicInteger(0);
119
120
121     /**
122      * The facade associated with this wrapper.
123      */

124     protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
125
126
127     /**
128      * The (single) possibly uninitialized instance of this servlet.
129      */

130     protected volatile Servlet instance = null;
131
132
133     /**
134      * Flag that indicates if this instance has been initialized
135      */

136     protected volatile boolean instanceInitialized = false;
137
138
139     /**
140      * The load-on-startup order value (negative value means load on
141      * first call) for this servlet.
142      */

143     protected int loadOnStartup = -1;
144
145
146     /**
147      * Mappings associated with the wrapper.
148      */

149     protected final ArrayList<String> mappings = new ArrayList<>();
150
151
152     /**
153      * The initialization parameters for this servlet, keyed by
154      * parameter name.
155      */

156     protected HashMap<String, String> parameters = new HashMap<>();
157
158
159     /**
160      * The security role references for this servlet, keyed by role name
161      * used in the servlet.  The corresponding value is the role name of
162      * the web application itself.
163      */

164     protected HashMap<String, String> references = new HashMap<>();
165
166
167     /**
168      * The run-as identity for this servlet.
169      */

170     protected String runAs = null;
171
172     /**
173      * The notification sequence number.
174      */

175     protected long sequenceNumber = 0;
176
177     /**
178      * The fully qualified servlet class name for this servlet.
179      */

180     protected String servletClass = null;
181
182
183     /**
184      * Does this servlet implement the SingleThreadModel interface?
185      */

186     protected volatile boolean singleThreadModel = false;
187
188
189     /**
190      * Are we unloading our servlet instance at the moment?
191      */

192     protected volatile boolean unloading = false;
193
194
195     /**
196      * Maximum number of STM instances.
197      */

198     protected int maxInstances = 20;
199
200
201     /**
202      * Number of instances currently loaded for a STM servlet.
203      */

204     protected int nInstances = 0;
205
206
207     /**
208      * Stack containing the STM instances.
209      */

210     protected Stack<Servlet> instancePool = null;
211
212
213     /**
214      * Wait time for servlet unload in ms.
215      */

216     protected long unloadDelay = 2000;
217
218
219     /**
220      * True if this StandardWrapper is for the JspServlet
221      */

222     protected boolean isJspServlet;
223
224
225     /**
226      * The ObjectName of the JSP monitoring mbean
227      */

228     protected ObjectName jspMonitorON;
229
230
231     /**
232      * Should we swallow System.out
233      */

234     protected boolean swallowOutput = false;
235
236     // To support jmx attributes
237     protected StandardWrapperValve swValve;
238     protected long loadTime=0;
239     protected int classLoadTime=0;
240
241     /**
242      * Multipart config
243      */

244     protected MultipartConfigElement multipartConfigElement = null;
245
246     /**
247      * Async support
248      */

249     protected boolean asyncSupported = false;
250
251     /**
252      * Enabled
253      */

254     protected boolean enabled = true;
255
256     private boolean overridable = false;
257
258     /**
259      * Static class array used when the SecurityManager is turned on and
260      * <code>Servlet.init</code> is invoked.
261      */

262     protected static Class<?>[] classType = new Class[]{ServletConfig.class};
263
264     private final ReentrantReadWriteLock parametersLock =
265             new ReentrantReadWriteLock();
266
267     private final ReentrantReadWriteLock mappingsLock =
268             new ReentrantReadWriteLock();
269
270     private final ReentrantReadWriteLock referencesLock =
271             new ReentrantReadWriteLock();
272
273
274     // ------------------------------------------------------------- Properties
275
276     @Override
277     public boolean isOverridable() {
278         return overridable;
279     }
280
281     @Override
282     public void setOverridable(boolean overridable) {
283         this.overridable = overridable;
284     }
285
286     /**
287      * Return the available date/time for this servlet, in milliseconds since
288      * the epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
289      * that unavailability is permanent and any request for this servlet will return
290      * an SC_NOT_FOUND error.  If this date/time is in the future, any request for
291      * this servlet will return an SC_SERVICE_UNAVAILABLE error.  If it is zero,
292      * the servlet is currently available.
293      */

294     @Override
295     public long getAvailable() {
296         return this.available;
297     }
298
299
300     /**
301      * Set the available date/time for this servlet, in milliseconds since the
302      * epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
303      * that unavailability is permanent and any request for this servlet will return
304      * an SC_NOT_FOUND error. If this date/time is in the future, any request for
305      * this servlet will return an SC_SERVICE_UNAVAILABLE error.
306      *
307      * @param available The new available date/time
308      */

309     @Override
310     public void setAvailable(long available) {
311         long oldAvailable = this.available;
312         if (available > System.currentTimeMillis())
313             this.available = available;
314         else
315             this.available = 0L;
316         support.firePropertyChange("available", Long.valueOf(oldAvailable),
317                                    Long.valueOf(this.available));
318     }
319
320
321     /**
322      * @return the number of active allocations of this servlet, even if they
323      * are all for the same instance (as will be true for servlets that do
324      * not implement <code>SingleThreadModel</code>.
325      */

326     public int getCountAllocated() {
327         return this.countAllocated.get();
328     }
329
330
331     /**
332      * @return the load-on-startup order value (negative value means
333      * load on first call).
334      */

335     @Override
336     public int getLoadOnStartup() {
337
338         if (isJspServlet && loadOnStartup < 0) {
339             /*
340              * JspServlet must always be preloaded, because its instance is
341              * used during registerJMX (when registering the JSP
342              * monitoring mbean)
343              */

344              return Integer.MAX_VALUE;
345         } else {
346             return this.loadOnStartup;
347         }
348     }
349
350
351     /**
352      * Set the load-on-startup order value (negative value means
353      * load on first call).
354      *
355      * @param value New load-on-startup value
356      */

357     @Override
358     public void setLoadOnStartup(int value) {
359
360         int oldLoadOnStartup = this.loadOnStartup;
361         this.loadOnStartup = value;
362         support.firePropertyChange("loadOnStartup",
363                                    Integer.valueOf(oldLoadOnStartup),
364                                    Integer.valueOf(this.loadOnStartup));
365
366     }
367
368
369
370     /**
371      * Set the load-on-startup order value from a (possibly null) string.
372      * Per the specification, any missing or non-numeric value is converted
373      * to a zero, so that this servlet will still be loaded at startup
374      * time, but in an arbitrary order.
375      *
376      * @param value New load-on-startup value
377      */

378     public void setLoadOnStartupString(String value) {
379
380         try {
381             setLoadOnStartup(Integer.parseInt(value));
382         } catch (NumberFormatException e) {
383             setLoadOnStartup(0);
384         }
385     }
386
387     /**
388      * @return the load-on-startup value that was parsed
389      */

390     public String getLoadOnStartupString() {
391         return Integer.toString( getLoadOnStartup());
392     }
393
394
395     /**
396      * @return maximum number of instances that will be allocated when a single
397      * thread model servlet is used.
398      */

399     public int getMaxInstances() {
400         return this.maxInstances;
401     }
402
403
404     /**
405      * Set the maximum number of instances that will be allocated when a single
406      * thread model servlet is used.
407      *
408      * @param maxInstances New value of maxInstances
409      */

410     public void setMaxInstances(int maxInstances) {
411
412         int oldMaxInstances = this.maxInstances;
413         this.maxInstances = maxInstances;
414         support.firePropertyChange("maxInstances", oldMaxInstances,
415                                    this.maxInstances);
416
417     }
418
419
420     /**
421      * Set the parent Container of this Wrapper, but only if it is a Context.
422      *
423      * @param container Proposed parent Container
424      */

425     @Override
426     public void setParent(Container container) {
427
428         if ((container != null) &&
429             !(container instanceof Context))
430             throw new IllegalArgumentException
431                 (sm.getString("standardWrapper.notContext"));
432         if (container instanceof StandardContext) {
433             swallowOutput = ((StandardContext)container).getSwallowOutput();
434             unloadDelay = ((StandardContext)container).getUnloadDelay();
435         }
436         super.setParent(container);
437
438     }
439
440
441     /**
442      * @return the run-as identity for this servlet.
443      */

444     @Override
445     public String getRunAs() {
446         return this.runAs;
447     }
448
449
450     /**
451      * Set the run-as identity for this servlet.
452      *
453      * @param runAs New run-as identity value
454      */

455     @Override
456     public void setRunAs(String runAs) {
457
458         String oldRunAs = this.runAs;
459         this.runAs = runAs;
460         support.firePropertyChange("runAs", oldRunAs, this.runAs);
461
462     }
463
464
465     /**
466      * @return the fully qualified servlet class name for this servlet.
467      */

468     @Override
469     public String getServletClass() {
470         return this.servletClass;
471     }
472
473
474     /**
475      * Set the fully qualified servlet class name for this servlet.
476      *
477      * @param servletClass Servlet class name
478      */

479     @Override
480     public void setServletClass(String servletClass) {
481
482         String oldServletClass = this.servletClass;
483         this.servletClass = servletClass;
484         support.firePropertyChange("servletClass", oldServletClass,
485                                    this.servletClass);
486         if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) {
487             isJspServlet = true;
488         }
489     }
490
491
492
493     /**
494      * Set the name of this servlet.  This is an alias for the normal
495      * <code>Container.setName()</code> method, and complements the
496      * <code>getServletName()</code> method required by the
497      * <code>ServletConfig</code> interface.
498      *
499      * @param name The new name of this servlet
500      */

501     public void setServletName(String name) {
502
503         setName(name);
504
505     }
506
507
508     /**
509      * Does the servlet class represented by this component implement the
510      * <code>SingleThreadModel</code> interface? This can only be determined
511      * once the class is loaded. Calling this method will not trigger loading
512      * the class since that may cause the application to behave unexpectedly.
513      *
514      * @return {@code nullif the class has not been loaded, otherwise {@code
515      *         trueif the servlet does implement {@code SingleThreadModel} and
516      *         {@code falseif it does not.
517      */

518     public Boolean isSingleThreadModel() {
519         // If the servlet has been loaded either singleThreadModel will be true
520         // or instance will be non-null
521         if (singleThreadModel || instance != null) {
522             return Boolean.valueOf(singleThreadModel);
523         }
524         return null;
525     }
526
527
528     /**
529      * @return <code>true</code> if the Servlet has been marked unavailable.
530      */

531     @Override
532     public boolean isUnavailable() {
533
534         if (!isEnabled())
535             return true;
536         else if (available == 0L)
537             return false;
538         else if (available <= System.currentTimeMillis()) {
539             available = 0L;
540             return false;
541         } else
542             return true;
543
544     }
545
546
547     @Override
548     public String[] getServletMethods() throws ServletException {
549
550         instance = loadServlet();
551
552         Class<? extends Servlet> servletClazz = instance.getClass();
553         if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
554                                                         servletClazz)) {
555             return DEFAULT_SERVLET_METHODS;
556         }
557
558         Set<String> allow = new HashSet<>();
559         allow.add("OPTIONS");
560
561         if (isJspServlet) {
562             allow.add("GET");
563             allow.add("HEAD");
564             allow.add("POST");
565         } else {
566             allow.add("TRACE");
567
568             Method[] methods = getAllDeclaredMethods(servletClazz);
569             for (int i=0; methods != null && i<methods.length; i++) {
570                 Method m = methods[i];
571
572                 if (m.getName().equals("doGet")) {
573                     allow.add("GET");
574                     allow.add("HEAD");
575                 } else if (m.getName().equals("doPost")) {
576                     allow.add("POST");
577                 } else if (m.getName().equals("doPut")) {
578                     allow.add("PUT");
579                 } else if (m.getName().equals("doDelete")) {
580                     allow.add("DELETE");
581                 }
582             }
583         }
584
585         String[] methodNames = new String[allow.size()];
586         return allow.toArray(methodNames);
587     }
588
589
590     /**
591      * @return the associated servlet instance.
592      */

593     @Override
594     public Servlet getServlet() {
595         return instance;
596     }
597
598
599     /**
600      * Set the associated servlet instance.
601      */

602     @Override
603     public void setServlet(Servlet servlet) {
604         instance = servlet;
605     }
606
607
608     // --------------------------------------------------------- Public Methods
609
610     /**
611      * Execute a periodic task, such as reloading, etc. This method will be
612      * invoked inside the classloading context of this container. Unexpected
613      * throwables will be caught and logged.
614      */

615     @Override
616     public void backgroundProcess() {
617         super.backgroundProcess();
618
619         if (!getState().isAvailable())
620             return;
621
622         if (getServlet() instanceof PeriodicEventListener) {
623             ((PeriodicEventListener) getServlet()).periodicEvent();
624         }
625     }
626
627
628     /**
629      * Extract the root cause from a servlet exception.
630      *
631      * @param e The servlet exception
632      * @return the root cause of the Servlet exception
633      */

634     public static Throwable getRootCause(ServletException e) {
635         Throwable rootCause = e;
636         Throwable rootCauseCheck = null;
637         // Extra aggressive rootCause finding
638         int loops = 0;
639         do {
640             loops++;
641             rootCauseCheck = rootCause.getCause();
642             if (rootCauseCheck != null)
643                 rootCause = rootCauseCheck;
644         } while (rootCauseCheck != null && (loops < 20));
645         return rootCause;
646     }
647
648
649     /**
650      * Refuse to add a child Container, because Wrappers are the lowest level
651      * of the Container hierarchy.
652      *
653      * @param child Child container to be added
654      */

655     @Override
656     public void addChild(Container child) {
657
658         throw new IllegalStateException
659             (sm.getString("standardWrapper.notChild"));
660
661     }
662
663
664     /**
665      * Add a new servlet initialization parameter for this servlet.
666      *
667      * @param name Name of this initialization parameter to add
668      * @param value Value of this initialization parameter to add
669      */

670     @Override
671     public void addInitParameter(String name, String value) {
672
673         parametersLock.writeLock().lock();
674         try {
675             parameters.put(name, value);
676         } finally {
677             parametersLock.writeLock().unlock();
678         }
679         fireContainerEvent("addInitParameter", name);
680
681     }
682
683
684     /**
685      * Add a mapping associated with the Wrapper.
686      *
687      * @param mapping The new wrapper mapping
688      */

689     @Override
690     public void addMapping(String mapping) {
691
692         mappingsLock.writeLock().lock();
693         try {
694             mappings.add(mapping);
695         } finally {
696             mappingsLock.writeLock().unlock();
697         }
698         if(parent.getState().equals(LifecycleState.STARTED))
699             fireContainerEvent(ADD_MAPPING_EVENT, mapping);
700
701     }
702
703
704     /**
705      * Add a new security role reference record to the set of records for
706      * this servlet.
707      *
708      * @param name Role name used within this servlet
709      * @param link Role name used within the web application
710      */

711     @Override
712     public void addSecurityReference(String name, String link) {
713
714         referencesLock.writeLock().lock();
715         try {
716             references.put(name, link);
717         } finally {
718             referencesLock.writeLock().unlock();
719         }
720         fireContainerEvent("addSecurityReference", name);
721
722     }
723
724
725     /**
726      * Allocate an initialized instance of this Servlet that is ready to have
727      * its <code>service()</code> method called.  If the servlet class does
728      * not implement <code>SingleThreadModel</code>, the (only) initialized
729      * instance may be returned immediately.  If the servlet class implements
730      * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
731      * that this instance is not allocated again until it is deallocated by a
732      * call to <code>deallocate()</code>.
733      *
734      * @exception ServletException if the servlet init() method threw
735      *  an exception
736      * @exception ServletException if a loading error occurs
737      */

738     @Override
739     public Servlet allocate() throws ServletException {
740
741         // If we are currently unloading this servlet, throw an exception
742         if (unloading) {
743             throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
744         }
745
746         boolean newInstance = false;
747
748         // If not SingleThreadedModel, return the same instance every time
749         if (!singleThreadModel) {
750             // Load and initialize our instance if necessary
751             if (instance == null || !instanceInitialized) {
752                 synchronized (this) {
753                     if (instance == null) {
754                         try {
755                             if (log.isDebugEnabled()) {
756                                 log.debug("Allocating non-STM instance");
757                             }
758
759                             // Note: We don't know if the Servlet implements
760                             // SingleThreadModel until we have loaded it.
761                             instance = loadServlet();
762                             newInstance = true;
763                             if (!singleThreadModel) {
764                                 // For non-STM, increment here to prevent a race
765                                 // condition with unload. Bug 43683, test case
766                                 // #3
767                                 countAllocated.incrementAndGet();
768                             }
769                         } catch (ServletException e) {
770                             throw e;
771                         } catch (Throwable e) {
772                             ExceptionUtils.handleThrowable(e);
773                             throw new ServletException(sm.getString("standardWrapper.allocate"), e);
774                         }
775                     }
776                     if (!instanceInitialized) {
777                         initServlet(instance);
778                     }
779                 }
780             }
781
782             if (singleThreadModel) {
783                 if (newInstance) {
784                     // Have to do this outside of the sync above to prevent a
785                     // possible deadlock
786                     synchronized (instancePool) {
787                         instancePool.push(instance);
788                         nInstances++;
789                     }
790                 }
791             } else {
792                 if (log.isTraceEnabled()) {
793                     log.trace("  Returning non-STM instance");
794                 }
795                 // For new instances, count will have been incremented at the
796                 // time of creation
797                 if (!newInstance) {
798                     countAllocated.incrementAndGet();
799                 }
800                 return instance;
801             }
802         }
803
804         synchronized (instancePool) {
805             while (countAllocated.get() >= nInstances) {
806                 // Allocate a new instance if possible, or else wait
807                 if (nInstances < maxInstances) {
808                     try {
809                         instancePool.push(loadServlet());
810                         nInstances++;
811                     } catch (ServletException e) {
812                         throw e;
813                     } catch (Throwable e) {
814                         ExceptionUtils.handleThrowable(e);
815                         throw new ServletException(sm.getString("standardWrapper.allocate"), e);
816                     }
817                 } else {
818                     try {
819                         instancePool.wait();
820                     } catch (InterruptedException e) {
821                         // Ignore
822                     }
823                 }
824             }
825             if (log.isTraceEnabled()) {
826                 log.trace("  Returning allocated STM instance");
827             }
828             countAllocated.incrementAndGet();
829             return instancePool.pop();
830         }
831     }
832
833
834     /**
835      * Return this previously allocated servlet to the pool of available
836      * instances.  If this servlet class does not implement SingleThreadModel,
837      * no action is actually required.
838      *
839      * @param servlet The servlet to be returned
840      *
841      * @exception ServletException if a deallocation error occurs
842      */

843     @Override
844     public void deallocate(Servlet servlet) throws ServletException {
845
846         // If not SingleThreadModel, no action is required
847         if (!singleThreadModel) {
848             countAllocated.decrementAndGet();
849             return;
850         }
851
852         // Unlock and free this instance
853         synchronized (instancePool) {
854             countAllocated.decrementAndGet();
855             instancePool.push(servlet);
856             instancePool.notify();
857         }
858
859     }
860
861
862     /**
863      * Return the value for the specified initialization parameter name,
864      * if any; otherwise return <code>null</code>.
865      *
866      * @param name Name of the requested initialization parameter
867      */

868     @Override
869     public String findInitParameter(String name) {
870
871         parametersLock.readLock().lock();
872         try {
873             return parameters.get(name);
874         } finally {
875             parametersLock.readLock().unlock();
876         }
877
878     }
879
880
881     /**
882      * Return the names of all defined initialization parameters for this
883      * servlet.
884      */

885     @Override
886     public String[] findInitParameters() {
887
888         parametersLock.readLock().lock();
889         try {
890             String results[] = new String[parameters.size()];
891             return parameters.keySet().toArray(results);
892         } finally {
893             parametersLock.readLock().unlock();
894         }
895
896     }
897
898
899     /**
900      * Return the mappings associated with this wrapper.
901      */

902     @Override
903     public String[] findMappings() {
904
905         mappingsLock.readLock().lock();
906         try {
907             return mappings.toArray(new String[mappings.size()]);
908         } finally {
909             mappingsLock.readLock().unlock();
910         }
911
912     }
913
914
915     /**
916      * Return the security role link for the specified security role
917      * reference name, if any; otherwise return <code>null</code>.
918      *
919      * @param name Security role reference used within this servlet
920      */

921     @Override
922     public String findSecurityReference(String name) {
923         String reference = null;
924
925         referencesLock.readLock().lock();
926         try {
927             reference = references.get(name);
928         } finally {
929             referencesLock.readLock().unlock();
930         }
931
932         // If not specified on the Wrapper, check the Context
933         if (getParent() instanceof Context) {
934             Context context = (Context) getParent();
935             if (reference != null) {
936                 reference = context.findRoleMapping(reference);
937             } else {
938                 reference = context.findRoleMapping(name);
939             }
940         }
941
942         return reference;
943     }
944
945
946     /**
947      * Return the set of security role reference names associated with
948      * this servlet, if any; otherwise return a zero-length array.
949      */

950     @Override
951     public String[] findSecurityReferences() {
952
953         referencesLock.readLock().lock();
954         try {
955             String results[] = new String[references.size()];
956             return references.keySet().toArray(results);
957         } finally {
958             referencesLock.readLock().unlock();
959         }
960
961     }
962
963
964     /**
965      * Load and initialize an instance of this servlet, if there is not already
966      * at least one initialized instance.  This can be used, for example, to
967      * load servlets that are marked in the deployment descriptor to be loaded
968      * at server startup time.
969      * <p>
970      * <b>IMPLEMENTATION NOTE</b>:  Servlets whose classnames begin with
971      * <code>org.apache.catalina.</code> (so-called "container" servlets)
972      * are loaded by the same classloader that loaded this class, rather than
973      * the classloader for the current web application.
974      * This gives such classes access to Catalina internals, which are
975      * prevented for classes loaded for web applications.
976      *
977      * @exception ServletException if the servlet init() method threw
978      *  an exception
979      * @exception ServletException if some other loading problem occurs
980      */

981     @Override
982     public synchronized void load() throws ServletException {
983         instance = loadServlet();
984
985         if (!instanceInitialized) {
986             initServlet(instance);
987         }
988
989         if (isJspServlet) {
990             StringBuilder oname = new StringBuilder(getDomain());
991
992             oname.append(":type=JspMonitor");
993
994             oname.append(getWebModuleKeyProperties());
995
996             oname.append(",name=");
997             oname.append(getName());
998
999             oname.append(getJ2EEKeyProperties());
1000
1001             try {
1002                 jspMonitorON = new ObjectName(oname.toString());
1003                 Registry.getRegistry(nullnull).registerComponent(instance, jspMonitorON, null);
1004             } catch (Exception ex) {
1005                 log.warn(sm.getString("standardWrapper.jspMonitorError", instance));
1006             }
1007         }
1008     }
1009
1010
1011     /**
1012      * Load and initialize an instance of this servlet, if there is not already
1013      * at least one initialized instance.  This can be used, for example, to
1014      * load servlets that are marked in the deployment descriptor to be loaded
1015      * at server startup time.
1016      * @return the loaded Servlet instance
1017      * @throws ServletException for a Servlet load error
1018      */

1019     public synchronized Servlet loadServlet() throws ServletException {
1020
1021         // Nothing to do if we already have an instance or an instance pool
1022         if (!singleThreadModel && (instance != null))
1023             return instance;
1024
1025         PrintStream out = System.out;
1026         if (swallowOutput) {
1027             SystemLogHandler.startCapture();
1028         }
1029
1030         Servlet servlet;
1031         try {
1032             long t1=System.currentTimeMillis();
1033             // Complain if no servlet class has been specified
1034             if (servletClass == null) {
1035                 unavailable(null);
1036                 throw new ServletException
1037                     (sm.getString("standardWrapper.notClass", getName()));
1038             }
1039
1040             InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
1041             try {
1042                 servlet = (Servlet) instanceManager.newInstance(servletClass);
1043             } catch (ClassCastException e) {
1044                 unavailable(null);
1045                 // Restore the context ClassLoader
1046                 throw new ServletException
1047                     (sm.getString("standardWrapper.notServlet", servletClass), e);
1048             } catch (Throwable e) {
1049                 e = ExceptionUtils.unwrapInvocationTargetException(e);
1050                 ExceptionUtils.handleThrowable(e);
1051                 unavailable(null);
1052
1053                 // Added extra log statement for Bugzilla 36630:
1054                 // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
1055                 if(log.isDebugEnabled()) {
1056                     log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
1057                 }
1058
1059                 // Restore the context ClassLoader
1060                 throw new ServletException
1061                     (sm.getString("standardWrapper.instantiate", servletClass), e);
1062             }
1063
1064             if (multipartConfigElement == null) {
1065                 MultipartConfig annotation =
1066                         servlet.getClass().getAnnotation(MultipartConfig.class);
1067                 if (annotation != null) {
1068                     multipartConfigElement =
1069                             new MultipartConfigElement(annotation);
1070                 }
1071             }
1072
1073             // Special handling for ContainerServlet instances
1074             // Note: The InstanceManager checks if the application is permitted
1075             //       to load ContainerServlets
1076             if (servlet instanceof ContainerServlet) {
1077                 ((ContainerServlet) servlet).setWrapper(this);
1078             }
1079
1080             classLoadTime=(int) (System.currentTimeMillis() -t1);
1081
1082             if (servlet instanceof SingleThreadModel) {
1083                 if (instancePool == null) {
1084                     instancePool = new Stack<>();
1085                 }
1086                 singleThreadModel = true;
1087             }
1088
1089             initServlet(servlet);
1090
1091             fireContainerEvent("load"this);
1092
1093             loadTime=System.currentTimeMillis() -t1;
1094         } finally {
1095             if (swallowOutput) {
1096                 String log = SystemLogHandler.stopCapture();
1097                 if (log != null && log.length() > 0) {
1098                     if (getServletContext() != null) {
1099                         getServletContext().log(log);
1100                     } else {
1101                         out.println(log);
1102                     }
1103                 }
1104             }
1105         }
1106         return servlet;
1107
1108     }
1109
1110
1111     private synchronized void initServlet(Servlet servlet)
1112             throws ServletException {
1113
1114         if (instanceInitialized && !singleThreadModel) return;
1115
1116         // Call the initialization method of this servlet
1117         try {
1118             if( Globals.IS_SECURITY_ENABLED) {
1119                 boolean success = false;
1120                 try {
1121                     Object[] args = new Object[] { facade };
1122                     SecurityUtil.doAsPrivilege("init",
1123                                                servlet,
1124                                                classType,
1125                                                args);
1126                     success = true;
1127                 } finally {
1128                     if (!success) {
1129                         // destroy() will not be called, thus clear the reference now
1130                         SecurityUtil.remove(servlet);
1131                     }
1132                 }
1133             } else {
1134                 servlet.init(facade);
1135             }
1136
1137             instanceInitialized = true;
1138         } catch (UnavailableException f) {
1139             unavailable(f);
1140             throw f;
1141         } catch (ServletException f) {
1142             // If the servlet wanted to be unavailable it would have
1143             // said so, so do not call unavailable(null).
1144             throw f;
1145         } catch (Throwable f) {
1146             ExceptionUtils.handleThrowable(f);
1147             getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
1148             // If the servlet wanted to be unavailable it would have
1149             // said so, so do not call unavailable(null).
1150             throw new ServletException
1151                 (sm.getString("standardWrapper.initException", getName()), f);
1152         }
1153     }
1154
1155     /**
1156      * Remove the specified initialization parameter from this servlet.
1157      *
1158      * @param name Name of the initialization parameter to remove
1159      */

1160     @Override
1161     public void removeInitParameter(String name) {
1162
1163         parametersLock.writeLock().lock();
1164         try {
1165             parameters.remove(name);
1166         } finally {
1167             parametersLock.writeLock().unlock();
1168         }
1169         fireContainerEvent("removeInitParameter", name);
1170
1171     }
1172
1173
1174     /**
1175      * Remove a mapping associated with the wrapper.
1176      *
1177      * @param mapping The pattern to remove
1178      */

1179     @Override
1180     public void removeMapping(String mapping) {
1181
1182         mappingsLock.writeLock().lock();
1183         try {
1184             mappings.remove(mapping);
1185         } finally {
1186             mappingsLock.writeLock().unlock();
1187         }
1188         if(parent.getState().equals(LifecycleState.STARTED))
1189             fireContainerEvent(REMOVE_MAPPING_EVENT, mapping);
1190
1191     }
1192
1193
1194     /**
1195      * Remove any security role reference for the specified role name.
1196      *
1197      * @param name Security role used within this servlet to be removed
1198      */

1199     @Override
1200     public void removeSecurityReference(String name) {
1201
1202         referencesLock.writeLock().lock();
1203         try {
1204             references.remove(name);
1205         } finally {
1206             referencesLock.writeLock().unlock();
1207         }
1208         fireContainerEvent("removeSecurityReference", name);
1209
1210     }
1211
1212
1213     /**
1214      * Process an UnavailableException, marking this servlet as unavailable
1215      * for the specified amount of time.
1216      *
1217      * @param unavailable The exception that occurred, or <code>null</code>
1218      *  to mark this servlet as permanently unavailable
1219      */

1220     @Override
1221     public void unavailable(UnavailableException unavailable) {
1222         getServletContext().log(sm.getString("standardWrapper.unavailable", getName()));
1223         if (unavailable == null)
1224             setAvailable(Long.MAX_VALUE);
1225         else if (unavailable.isPermanent())
1226             setAvailable(Long.MAX_VALUE);
1227         else {
1228             int unavailableSeconds = unavailable.getUnavailableSeconds();
1229             if (unavailableSeconds <= 0)
1230                 unavailableSeconds = 60;        // Arbitrary default
1231             setAvailable(System.currentTimeMillis() +
1232                          (unavailableSeconds * 1000L));
1233         }
1234
1235     }
1236
1237
1238     /**
1239      * Unload all initialized instances of this servlet, after calling the
1240      * <code>destroy()</code> method for each instance.  This can be used,
1241      * for example, prior to shutting down the entire servlet engine, or
1242      * prior to reloading all of the classes from the Loader associated with
1243      * our Loader's repository.
1244      *
1245      * @exception ServletException if an exception is thrown by the
1246      *  destroy() method
1247      */

1248     @Override
1249     public synchronized void unload() throws ServletException {
1250
1251         // Nothing to do if we have never loaded the instance
1252         if (!singleThreadModel && (instance == null))
1253             return;
1254         unloading = true;
1255
1256         // Loaf a while if the current instance is allocated
1257         // (possibly more than once if non-STM)
1258         if (countAllocated.get() > 0) {
1259             int nRetries = 0;
1260             long delay = unloadDelay / 20;
1261             while ((nRetries < 21) && (countAllocated.get() > 0)) {
1262                 if ((nRetries % 10) == 0) {
1263                     log.info(sm.getString("standardWrapper.waiting",
1264                                           countAllocated.toString(),
1265                                           getName()));
1266                 }
1267                 try {
1268                     Thread.sleep(delay);
1269                 } catch (InterruptedException e) {
1270                     // Ignore
1271                 }
1272                 nRetries++;
1273             }
1274         }
1275
1276         if (instanceInitialized) {
1277             PrintStream out = System.out;
1278             if (swallowOutput) {
1279                 SystemLogHandler.startCapture();
1280             }
1281
1282             // Call the servlet destroy() method
1283             try {
1284                 if( Globals.IS_SECURITY_ENABLED) {
1285                     try {
1286                         SecurityUtil.doAsPrivilege("destroy", instance);
1287                     } finally {
1288                         SecurityUtil.remove(instance);
1289                     }
1290                 } else {
1291                     instance.destroy();
1292                 }
1293
1294             } catch (Throwable t) {
1295                 t = ExceptionUtils.unwrapInvocationTargetException(t);
1296                 ExceptionUtils.handleThrowable(t);
1297                 instance = null;
1298                 instancePool = null;
1299                 nInstances = 0;
1300                 fireContainerEvent("unload"this);
1301                 unloading = false;
1302                 throw new ServletException
1303                     (sm.getString("standardWrapper.destroyException", getName()),
1304                      t);
1305             } finally {
1306                 // Annotation processing
1307                 if (!((Context) getParent()).getIgnoreAnnotations()) {
1308                     try {
1309                         ((Context)getParent()).getInstanceManager().destroyInstance(instance);
1310                     } catch (Throwable t) {
1311                         ExceptionUtils.handleThrowable(t);
1312                         log.error(sm.getString("standardWrapper.destroyInstance", getName()), t);
1313                     }
1314                 }
1315                 // Write captured output
1316                 if (swallowOutput) {
1317                     String log = SystemLogHandler.stopCapture();
1318                     if (log != null && log.length() > 0) {
1319                         if (getServletContext() != null) {
1320                             getServletContext().log(log);
1321                         } else {
1322                             out.println(log);
1323                         }
1324                     }
1325                 }
1326             }
1327         }
1328
1329         // Deregister the destroyed instance
1330         instance = null;
1331         instanceInitialized = false;
1332
1333         if (isJspServlet && jspMonitorON != null ) {
1334             Registry.getRegistry(nullnull).unregisterComponent(jspMonitorON);
1335         }
1336
1337         if (singleThreadModel && (instancePool != null)) {
1338             try {
1339                 while (!instancePool.isEmpty()) {
1340                     Servlet s = instancePool.pop();
1341                     if (Globals.IS_SECURITY_ENABLED) {
1342                         try {
1343                             SecurityUtil.doAsPrivilege("destroy", s);
1344                         } finally {
1345                             SecurityUtil.remove(s);
1346                         }
1347                     } else {
1348                         s.destroy();
1349                     }
1350                     // Annotation processing
1351                     if (!((Context) getParent()).getIgnoreAnnotations()) {
1352                        ((StandardContext)getParent()).getInstanceManager().destroyInstance(s);
1353                     }
1354                 }
1355             } catch (Throwable t) {
1356                 t = ExceptionUtils.unwrapInvocationTargetException(t);
1357                 ExceptionUtils.handleThrowable(t);
1358                 instancePool = null;
1359                 nInstances = 0;
1360                 unloading = false;
1361                 fireContainerEvent("unload"this);
1362                 throw new ServletException
1363                     (sm.getString("standardWrapper.destroyException",
1364                                   getName()), t);
1365             }
1366             instancePool = null;
1367             nInstances = 0;
1368         }
1369
1370         singleThreadModel = false;
1371
1372         unloading = false;
1373         fireContainerEvent("unload"this);
1374
1375     }
1376
1377
1378     // -------------------------------------------------- ServletConfig Methods
1379
1380
1381     /**
1382      * @return the initialization parameter value for the specified name,
1383      * if any; otherwise return <code>null</code>.
1384      *
1385      * @param name Name of the initialization parameter to retrieve
1386      */

1387     @Override
1388     public String getInitParameter(String name) {
1389         return findInitParameter(name);
1390     }
1391
1392
1393     /**
1394      * @return the set of initialization parameter names defined for this
1395      * servlet.  If none are defined, an empty Enumeration is returned.
1396      */

1397     @Override
1398     public Enumeration<String> getInitParameterNames() {
1399
1400         parametersLock.readLock().lock();
1401         try {
1402             return Collections.enumeration(parameters.keySet());
1403         } finally {
1404             parametersLock.readLock().unlock();
1405         }
1406
1407     }
1408
1409
1410     /**
1411      * @return the servlet context with which this servlet is associated.
1412      */

1413     @Override
1414     public ServletContext getServletContext() {
1415         if (parent == null)
1416             return null;
1417         else if (!(parent instanceof Context))
1418             return null;
1419         else
1420             return ((Context) parent).getServletContext();
1421     }
1422
1423
1424     /**
1425      * @return the name of this servlet.
1426      */

1427     @Override
1428     public String getServletName() {
1429         return getName();
1430     }
1431
1432     public long getProcessingTime() {
1433         return swValve.getProcessingTime();
1434     }
1435
1436     public long getMaxTime() {
1437         return swValve.getMaxTime();
1438     }
1439
1440     public long getMinTime() {
1441         return swValve.getMinTime();
1442     }
1443
1444     public int getRequestCount() {
1445         return swValve.getRequestCount();
1446     }
1447
1448     public int getErrorCount() {
1449         return swValve.getErrorCount();
1450     }
1451
1452     /**
1453      * Increment the error count used for monitoring.
1454      */

1455     @Override
1456     public void incrementErrorCount(){
1457         swValve.incrementErrorCount();
1458     }
1459
1460     public long getLoadTime() {
1461         return loadTime;
1462     }
1463
1464     public int getClassLoadTime() {
1465         return classLoadTime;
1466     }
1467
1468     @Override
1469     public MultipartConfigElement getMultipartConfigElement() {
1470         return multipartConfigElement;
1471     }
1472
1473     @Override
1474     public void setMultipartConfigElement(
1475             MultipartConfigElement multipartConfigElement) {
1476         this.multipartConfigElement = multipartConfigElement;
1477     }
1478
1479     @Override
1480     public boolean isAsyncSupported() {
1481         return asyncSupported;
1482     }
1483
1484     @Override
1485     public void setAsyncSupported(boolean asyncSupported) {
1486         this.asyncSupported = asyncSupported;
1487     }
1488
1489     @Override
1490     public boolean isEnabled() {
1491         return enabled;
1492     }
1493
1494     @Override
1495     public void setEnabled(boolean enabled) {
1496         this.enabled = enabled;
1497     }
1498
1499     // -------------------------------------------------------- Package Methods
1500
1501
1502     // -------------------------------------------------------- protected Methods
1503
1504
1505     protected Method[] getAllDeclaredMethods(Class<?> c) {
1506
1507         if (c.equals(javax.servlet.http.HttpServlet.class)) {
1508             return null;
1509         }
1510
1511         Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
1512
1513         Method[] thisMethods = c.getDeclaredMethods();
1514         if (thisMethods.length == 0) {
1515             return parentMethods;
1516         }
1517
1518         if ((parentMethods != null) && (parentMethods.length > 0)) {
1519             Method[] allMethods =
1520                 new Method[parentMethods.length + thisMethods.length];
1521             System.arraycopy(parentMethods, 0, allMethods, 0,
1522                              parentMethods.length);
1523             System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
1524                              thisMethods.length);
1525
1526             thisMethods = allMethods;
1527         }
1528
1529         return thisMethods;
1530     }
1531
1532
1533     // ------------------------------------------------------ Lifecycle Methods
1534
1535
1536     /**
1537      * Start this component and implement the requirements
1538      * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
1539      *
1540      * @exception LifecycleException if this component detects a fatal error
1541      *  that prevents this component from being used
1542      */

1543     @Override
1544     protected synchronized void startInternal() throws LifecycleException {
1545
1546         // Send j2ee.state.starting notification
1547         if (this.getObjectName() != null) {
1548             Notification notification = new Notification("j2ee.state.starting",
1549                                                         this.getObjectName(),
1550                                                         sequenceNumber++);
1551             broadcaster.sendNotification(notification);
1552         }
1553
1554         // Start up this component
1555         super.startInternal();
1556
1557         setAvailable(0L);
1558
1559         // Send j2ee.state.running notification
1560         if (this.getObjectName() != null) {
1561             Notification notification =
1562                 new Notification("j2ee.state.running"this.getObjectName(),
1563                                 sequenceNumber++);
1564             broadcaster.sendNotification(notification);
1565         }
1566
1567     }
1568
1569
1570     /**
1571      * Stop this component and implement the requirements
1572      * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
1573      *
1574      * @exception LifecycleException if this component detects a fatal error
1575      *  that prevents this component from being used
1576      */

1577     @Override
1578     protected synchronized void stopInternal() throws LifecycleException {
1579
1580         setAvailable(Long.MAX_VALUE);
1581
1582         // Send j2ee.state.stopping notification
1583         if (this.getObjectName() != null) {
1584             Notification notification =
1585                 new Notification("j2ee.state.stopping"this.getObjectName(),
1586                                 sequenceNumber++);
1587             broadcaster.sendNotification(notification);
1588         }
1589
1590         // Shut down our servlet instance (if it has been initialized)
1591         try {
1592             unload();
1593         } catch (ServletException e) {
1594             getServletContext().log(sm.getString
1595                       ("standardWrapper.unloadException", getName()), e);
1596         }
1597
1598         // Shut down this component
1599         super.stopInternal();
1600
1601         // Send j2ee.state.stopped notification
1602         if (this.getObjectName() != null) {
1603             Notification notification =
1604                 new Notification("j2ee.state.stopped"this.getObjectName(),
1605                                 sequenceNumber++);
1606             broadcaster.sendNotification(notification);
1607         }
1608
1609         // Send j2ee.object.deleted notification
1610         Notification notification =
1611             new Notification("j2ee.object.deleted"this.getObjectName(),
1612                             sequenceNumber++);
1613         broadcaster.sendNotification(notification);
1614
1615     }
1616
1617
1618     @Override
1619     protected String getObjectNameKeyProperties() {
1620
1621         StringBuilder keyProperties =
1622             new StringBuilder("j2eeType=Servlet");
1623
1624         keyProperties.append(getWebModuleKeyProperties());
1625
1626         keyProperties.append(",name=");
1627
1628         String name = getName();
1629         if (Util.objectNameValueNeedsQuote(name)) {
1630             name = ObjectName.quote(name);
1631         }
1632         keyProperties.append(name);
1633
1634         keyProperties.append(getJ2EEKeyProperties());
1635
1636         return keyProperties.toString();
1637     }
1638
1639
1640     private String getWebModuleKeyProperties() {
1641
1642         StringBuilder keyProperties = new StringBuilder(",WebModule=//");
1643         String hostName = getParent().getParent().getName();
1644         if (hostName == null) {
1645             keyProperties.append("DEFAULT");
1646         } else {
1647             keyProperties.append(hostName);
1648         }
1649
1650         String contextName = getParent().getName();
1651         if (!contextName.startsWith("/")) {
1652             keyProperties.append('/');
1653         }
1654         keyProperties.append(contextName);
1655
1656         return keyProperties.toString();
1657     }
1658
1659     private String getJ2EEKeyProperties() {
1660
1661         StringBuilder keyProperties = new StringBuilder(",J2EEApplication=");
1662
1663         StandardContext ctx = null;
1664         if (parent instanceof StandardContext) {
1665             ctx = (StandardContext) getParent();
1666         }
1667
1668         if (ctx == null) {
1669             keyProperties.append("none");
1670         } else {
1671             keyProperties.append(ctx.getJ2EEApplication());
1672         }
1673         keyProperties.append(",J2EEServer=");
1674         if (ctx == null) {
1675             keyProperties.append("none");
1676         } else {
1677             keyProperties.append(ctx.getJ2EEServer());
1678         }
1679
1680         return keyProperties.toString();
1681     }
1682
1683
1684     /**
1685      * Remove a JMX notificationListener
1686      * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
1687      */

1688     @Override
1689     public void removeNotificationListener(NotificationListener listener,
1690             NotificationFilter filter, Object object) throws ListenerNotFoundException {
1691         broadcaster.removeNotificationListener(listener,filter,object);
1692     }
1693
1694     protected MBeanNotificationInfo[] notificationInfo;
1695
1696     /**
1697      * Get JMX Broadcaster Info
1698      * FIXME: This two events we not send j2ee.state.failed and j2ee.attribute.changed!
1699      * @see javax.management.NotificationBroadcaster#getNotificationInfo()
1700      */

1701     @Override
1702     public MBeanNotificationInfo[] getNotificationInfo() {
1703
1704         if(notificationInfo == null) {
1705             notificationInfo = new MBeanNotificationInfo[]{
1706                     new MBeanNotificationInfo(new String[] {
1707                     "j2ee.object.created"},
1708                     Notification.class.getName(),
1709                     "servlet is created"
1710                     ),
1711                     new MBeanNotificationInfo(new String[] {
1712                     "j2ee.state.starting"},
1713                     Notification.class.getName(),
1714                     "servlet is starting"
1715                     ),
1716                     new MBeanNotificationInfo(new String[] {
1717                     "j2ee.state.running"},
1718                     Notification.class.getName(),
1719                     "servlet is running"
1720                     ),
1721                     new MBeanNotificationInfo(new String[] {
1722                     "j2ee.state.stopped"},
1723                     Notification.class.getName(),
1724                     "servlet start to stopped"
1725                     ),
1726                     new MBeanNotificationInfo(new String[] {
1727                     "j2ee.object.stopped"},
1728                     Notification.class.getName(),
1729                     "servlet is stopped"
1730                     ),
1731                     new MBeanNotificationInfo(new String[] {
1732                     "j2ee.object.deleted"},
1733                     Notification.class.getName(),
1734                     "servlet is deleted"
1735                     )
1736             };
1737         }
1738
1739         return notificationInfo;
1740     }
1741
1742
1743     /**
1744      * Add a JMX-NotificationListener
1745      * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
1746      */

1747     @Override
1748     public void addNotificationListener(NotificationListener listener,
1749             NotificationFilter filter, Object object) throws IllegalArgumentException {
1750         broadcaster.addNotificationListener(listener,filter,object);
1751     }
1752
1753
1754     /**
1755      * Remove a JMX-NotificationListener
1756      * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
1757      */

1758     @Override
1759     public void removeNotificationListener(NotificationListener listener)
1760         throws ListenerNotFoundException {
1761         broadcaster.removeNotificationListener(listener);
1762     }
1763 }
1764