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 null} if the class has not been loaded, otherwise {@code
515 * true} if the servlet does implement {@code SingleThreadModel} and
516 * {@code false} if 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(null, null).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(null, null).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