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
18
19 package org.apache.catalina.core;
20
21 import java.lang.reflect.Method;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.PrintStream;
25 import java.util.ArrayList;
26 import java.util.Enumeration;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Properties;
30 import java.util.Stack;
31 import java.util.concurrent.atomic.AtomicInteger;
32 import java.security.AccessController;
33 import java.security.PrivilegedActionException;
34 import java.security.PrivilegedExceptionAction;
35 import javax.servlet.Servlet;
36 import javax.servlet.ServletConfig;
37 import javax.servlet.ServletContext;
38 import javax.servlet.ServletException;
39 import javax.servlet.ServletRequest;
40 import javax.servlet.ServletResponse;
41 import javax.servlet.SingleThreadModel;
42 import javax.servlet.UnavailableException;
43 import javax.management.ListenerNotFoundException;
44 import javax.management.MBeanNotificationInfo;
45 import javax.management.Notification;
46 import javax.management.NotificationBroadcasterSupport;
47 import javax.management.NotificationEmitter;
48 import javax.management.NotificationFilter;
49 import javax.management.NotificationListener;
50 import javax.management.ObjectName;
51
52 import org.apache.PeriodicEventListener;
53 import org.apache.catalina.Container;
54 import org.apache.catalina.ContainerServlet;
55 import org.apache.catalina.Context;
56 import org.apache.catalina.Globals;
57 import org.apache.catalina.InstanceEvent;
58 import org.apache.catalina.InstanceListener;
59 import org.apache.catalina.LifecycleException;
60 import org.apache.catalina.Loader;
61 import org.apache.catalina.Wrapper;
62 import org.apache.catalina.security.SecurityUtil;
63 import org.apache.catalina.util.Enumerator;
64 import org.apache.catalina.util.InstanceSupport;
65 import org.apache.tomcat.util.log.SystemLogHandler;
66 import org.apache.tomcat.util.modeler.Registry;
67
68 /**
69 * Standard implementation of the <b>Wrapper</b> interface that represents
70 * an individual servlet definition. No child Containers are allowed, and
71 * the parent Container must be a Context.
72 *
73 * @author Craig R. McClanahan
74 * @author Remy Maucherat
75 * @version $Revision: 771014 $ $Date: 2009-05-03 03:33:01 +0200 (Sun, 03 May 2009) $
76 */
77 public class StandardWrapper
78 extends ContainerBase
79 implements ServletConfig, Wrapper, NotificationEmitter {
80
81 protected static org.apache.juli.logging.Log log=
82 org.apache.juli.logging.LogFactory.getLog( StandardWrapper.class );
83
84 protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
85 "GET", "HEAD", "POST" };
86
87 // ----------------------------------------------------------- Constructors
88
89
90 /**
91 * Create a new StandardWrapper component with the default basic Valve.
92 */
93 public StandardWrapper() {
94
95 super();
96 swValve=new StandardWrapperValve();
97 pipeline.setBasic(swValve);
98 broadcaster = new NotificationBroadcasterSupport();
99
100 if (restrictedServlets == null) {
101 restrictedServlets = new Properties();
102 try {
103 InputStream is =
104 this.getClass().getClassLoader().getResourceAsStream
105 ("org/apache/catalina/core/RestrictedServlets.properties");
106 if (is != null) {
107 restrictedServlets.load(is);
108 } else {
109 log.error(sm.getString("standardWrapper.restrictedServletsResource"));
110 }
111 } catch (IOException e) {
112 log.error(sm.getString("standardWrapper.restrictedServletsResource"), e);
113 }
114 }
115
116 }
117
118
119 // ----------------------------------------------------- Instance Variables
120
121
122 /**
123 * The date and time at which this servlet will become available (in
124 * milliseconds since the epoch), or zero if the servlet is available.
125 * If this value equals Long.MAX_VALUE, the unavailability of this
126 * servlet is considered permanent.
127 */
128 protected long available = 0L;
129
130 /**
131 * The broadcaster that sends j2ee notifications.
132 */
133 protected NotificationBroadcasterSupport broadcaster = null;
134
135 /**
136 * The count of allocations that are currently active (even if they
137 * are for the same instance, as will be true on a non-STM servlet).
138 */
139 protected AtomicInteger countAllocated = new AtomicInteger(0);
140
141
142 /**
143 * The facade associated with this wrapper.
144 */
145 protected StandardWrapperFacade facade =
146 new StandardWrapperFacade(this);
147
148
149 /**
150 * The descriptive information string for this implementation.
151 */
152 protected static final String info =
153 "org.apache.catalina.core.StandardWrapper/1.0";
154
155
156 /**
157 * The (single) initialized instance of this servlet.
158 */
159 protected Servlet instance = null;
160
161
162 /**
163 * The support object for our instance listeners.
164 */
165 protected InstanceSupport instanceSupport = new InstanceSupport(this);
166
167
168 /**
169 * The context-relative URI of the JSP file for this servlet.
170 */
171 protected String jspFile = null;
172
173
174 /**
175 * The load-on-startup order value (negative value means load on
176 * first call) for this servlet.
177 */
178 protected int loadOnStartup = -1;
179
180
181 /**
182 * Mappings associated with the wrapper.
183 */
184 protected ArrayList mappings = new ArrayList();
185
186
187 /**
188 * The initialization parameters for this servlet, keyed by
189 * parameter name.
190 */
191 protected HashMap parameters = new HashMap();
192
193
194 /**
195 * The security role references for this servlet, keyed by role name
196 * used in the servlet. The corresponding value is the role name of
197 * the web application itself.
198 */
199 protected HashMap references = new HashMap();
200
201
202 /**
203 * The run-as identity for this servlet.
204 */
205 protected String runAs = null;
206
207 /**
208 * The notification sequence number.
209 */
210 protected long sequenceNumber = 0;
211
212 /**
213 * The fully qualified servlet class name for this servlet.
214 */
215 protected String servletClass = null;
216
217
218 /**
219 * Does this servlet implement the SingleThreadModel interface?
220 */
221 protected boolean singleThreadModel = false;
222
223
224 /**
225 * Are we unloading our servlet instance at the moment?
226 */
227 protected boolean unloading = false;
228
229
230 /**
231 * Maximum number of STM instances.
232 */
233 protected int maxInstances = 20;
234
235
236 /**
237 * Number of instances currently loaded for a STM servlet.
238 */
239 protected int nInstances = 0;
240
241
242 /**
243 * Stack containing the STM instances.
244 */
245 protected Stack instancePool = null;
246
247
248 /**
249 * Wait time for servlet unload in ms.
250 */
251 protected long unloadDelay = 2000;
252
253
254 /**
255 * True if this StandardWrapper is for the JspServlet
256 */
257 protected boolean isJspServlet;
258
259
260 /**
261 * The ObjectName of the JSP monitoring mbean
262 */
263 protected ObjectName jspMonitorON;
264
265
266 /**
267 * Should we swallow System.out
268 */
269 protected boolean swallowOutput = false;
270
271 // To support jmx attributes
272 protected StandardWrapperValve swValve;
273 protected long loadTime=0;
274 protected int classLoadTime=0;
275
276 /**
277 * Static class array used when the SecurityManager is turned on and
278 * <code>Servlet.init</code> is invoked.
279 */
280 protected static Class[] classType = new Class[]{ServletConfig.class};
281
282
283 /**
284 * Static class array used when the SecurityManager is turned on and
285 * <code>Servlet.service</code> is invoked.
286 */
287 protected static Class[] classTypeUsedInService = new Class[]{
288 ServletRequest.class,
289 ServletResponse.class};
290
291 /**
292 * Restricted servlets (which can only be loaded by a privileged webapp).
293 */
294 protected static Properties restrictedServlets = null;
295
296
297 // ------------------------------------------------------------- Properties
298
299
300 /**
301 * Return the available date/time for this servlet, in milliseconds since
302 * the 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. If it is zero,
306 * the servlet is currently available.
307 */
308 public long getAvailable() {
309
310 return (this.available);
311
312 }
313
314
315 /**
316 * Set the available date/time for this servlet, in milliseconds since the
317 * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
318 * that unavailability is permanent and any request for this servlet will return
319 * an SC_NOT_FOUND error. If this date/time is in the future, any request for
320 * this servlet will return an SC_SERVICE_UNAVAILABLE error.
321 *
322 * @param available The new available date/time
323 */
324 public void setAvailable(long available) {
325
326 long oldAvailable = this.available;
327 if (available > System.currentTimeMillis())
328 this.available = available;
329 else
330 this.available = 0L;
331 support.firePropertyChange("available", new Long(oldAvailable),
332 new Long(this.available));
333
334 }
335
336
337 /**
338 * Return the number of active allocations of this servlet, even if they
339 * are all for the same instance (as will be true for servlets that do
340 * not implement <code>SingleThreadModel</code>.
341 */
342 public int getCountAllocated() {
343
344 return (this.countAllocated.get());
345
346 }
347
348
349 public String getEngineName() {
350 return ((StandardContext)getParent()).getEngineName();
351 }
352
353
354 /**
355 * Return descriptive information about this Container implementation and
356 * the corresponding version number, in the format
357 * <code><description>/<version></code>.
358 */
359 public String getInfo() {
360
361 return (info);
362
363 }
364
365
366 /**
367 * Return the InstanceSupport object for this Wrapper instance.
368 */
369 public InstanceSupport getInstanceSupport() {
370
371 return (this.instanceSupport);
372
373 }
374
375
376 /**
377 * Return the context-relative URI of the JSP file for this servlet.
378 */
379 public String getJspFile() {
380
381 return (this.jspFile);
382
383 }
384
385
386 /**
387 * Set the context-relative URI of the JSP file for this servlet.
388 *
389 * @param jspFile JSP file URI
390 */
391 public void setJspFile(String jspFile) {
392
393 String oldJspFile = this.jspFile;
394 this.jspFile = jspFile;
395 support.firePropertyChange("jspFile", oldJspFile, this.jspFile);
396
397 // Each jsp-file needs to be represented by its own JspServlet and
398 // corresponding JspMonitoring mbean, because it may be initialized
399 // with its own init params
400 isJspServlet = true;
401
402 }
403
404
405 /**
406 * Return the load-on-startup order value (negative value means
407 * load on first call).
408 */
409 public int getLoadOnStartup() {
410
411 if (isJspServlet && loadOnStartup < 0) {
412 /*
413 * JspServlet must always be preloaded, because its instance is
414 * used during registerJMX (when registering the JSP
415 * monitoring mbean)
416 */
417 return Integer.MAX_VALUE;
418 } else {
419 return (this.loadOnStartup);
420 }
421 }
422
423
424 /**
425 * Set the load-on-startup order value (negative value means
426 * load on first call).
427 *
428 * @param value New load-on-startup value
429 */
430 public void setLoadOnStartup(int value) {
431
432 int oldLoadOnStartup = this.loadOnStartup;
433 this.loadOnStartup = value;
434 support.firePropertyChange("loadOnStartup",
435 new Integer(oldLoadOnStartup),
436 new Integer(this.loadOnStartup));
437
438 }
439
440
441
442 /**
443 * Set the load-on-startup order value from a (possibly null) string.
444 * Per the specification, any missing or non-numeric value is converted
445 * to a zero, so that this servlet will still be loaded at startup
446 * time, but in an arbitrary order.
447 *
448 * @param value New load-on-startup value
449 */
450 public void setLoadOnStartupString(String value) {
451
452 try {
453 setLoadOnStartup(Integer.parseInt(value));
454 } catch (NumberFormatException e) {
455 setLoadOnStartup(0);
456 }
457 }
458
459 public String getLoadOnStartupString() {
460 return Integer.toString( getLoadOnStartup());
461 }
462
463
464 /**
465 * Return maximum number of instances that will be allocated when a single
466 * thread model servlet is used.
467 */
468 public int getMaxInstances() {
469
470 return (this.maxInstances);
471
472 }
473
474
475 /**
476 * Set the maximum number of instances that will be allocated when a single
477 * thread model servlet is used.
478 *
479 * @param maxInstances New value of maxInstances
480 */
481 public void setMaxInstances(int maxInstances) {
482
483 int oldMaxInstances = this.maxInstances;
484 this.maxInstances = maxInstances;
485 support.firePropertyChange("maxInstances", oldMaxInstances,
486 this.maxInstances);
487
488 }
489
490
491 /**
492 * Set the parent Container of this Wrapper, but only if it is a Context.
493 *
494 * @param container Proposed parent Container
495 */
496 public void setParent(Container container) {
497
498 if ((container != null) &&
499 !(container instanceof Context))
500 throw new IllegalArgumentException
501 (sm.getString("standardWrapper.notContext"));
502 if (container instanceof StandardContext) {
503 swallowOutput = ((StandardContext)container).getSwallowOutput();
504 unloadDelay = ((StandardContext)container).getUnloadDelay();
505 }
506 super.setParent(container);
507
508 }
509
510
511 /**
512 * Return the run-as identity for this servlet.
513 */
514 public String getRunAs() {
515
516 return (this.runAs);
517
518 }
519
520
521 /**
522 * Set the run-as identity for this servlet.
523 *
524 * @param runAs New run-as identity value
525 */
526 public void setRunAs(String runAs) {
527
528 String oldRunAs = this.runAs;
529 this.runAs = runAs;
530 support.firePropertyChange("runAs", oldRunAs, this.runAs);
531
532 }
533
534
535 /**
536 * Return the fully qualified servlet class name for this servlet.
537 */
538 public String getServletClass() {
539
540 return (this.servletClass);
541
542 }
543
544
545 /**
546 * Set the fully qualified servlet class name for this servlet.
547 *
548 * @param servletClass Servlet class name
549 */
550 public void setServletClass(String servletClass) {
551
552 String oldServletClass = this.servletClass;
553 this.servletClass = servletClass;
554 support.firePropertyChange("servletClass", oldServletClass,
555 this.servletClass);
556 if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) {
557 isJspServlet = true;
558 }
559 }
560
561
562
563 /**
564 * Set the name of this servlet. This is an alias for the normal
565 * <code>Container.setName()</code> method, and complements the
566 * <code>getServletName()</code> method required by the
567 * <code>ServletConfig</code> interface.
568 *
569 * @param name The new name of this servlet
570 */
571 public void setServletName(String name) {
572
573 setName(name);
574
575 }
576
577
578 /**
579 * Return <code>true</code> if the servlet class represented by this
580 * component implements the <code>SingleThreadModel</code> interface.
581 */
582 public boolean isSingleThreadModel() {
583
584 try {
585 loadServlet();
586 } catch (Throwable t) {
587 ;
588 }
589 return (singleThreadModel);
590
591 }
592
593
594 /**
595 * Is this servlet currently unavailable?
596 */
597 public boolean isUnavailable() {
598
599 if (available == 0L)
600 return (false);
601 else if (available <= System.currentTimeMillis()) {
602 available = 0L;
603 return (false);
604 } else
605 return (true);
606
607 }
608
609
610 /**
611 * Gets the names of the methods supported by the underlying servlet.
612 *
613 * This is the same set of methods included in the Allow response header
614 * in response to an OPTIONS request method processed by the underlying
615 * servlet.
616 *
617 * @return Array of names of the methods supported by the underlying
618 * servlet
619 */
620 public String[] getServletMethods() throws ServletException {
621
622 Class servletClazz = loadServlet().getClass();
623 if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
624 servletClazz)) {
625 return DEFAULT_SERVLET_METHODS;
626 }
627
628 HashSet allow = new HashSet();
629 allow.add("TRACE");
630 allow.add("OPTIONS");
631
632 Method[] methods = getAllDeclaredMethods(servletClazz);
633 for (int i=0; methods != null && i<methods.length; i++) {
634 Method m = methods[i];
635
636 if (m.getName().equals("doGet")) {
637 allow.add("GET");
638 allow.add("HEAD");
639 } else if (m.getName().equals("doPost")) {
640 allow.add("POST");
641 } else if (m.getName().equals("doPut")) {
642 allow.add("PUT");
643 } else if (m.getName().equals("doDelete")) {
644 allow.add("DELETE");
645 }
646 }
647
648 String[] methodNames = new String[allow.size()];
649 return (String[]) allow.toArray(methodNames);
650
651 }
652
653
654 // --------------------------------------------------------- Public Methods
655
656
657 /**
658 * Execute a periodic task, such as reloading, etc. This method will be
659 * invoked inside the classloading context of this container. Unexpected
660 * throwables will be caught and logged.
661 */
662 public void backgroundProcess() {
663 super.backgroundProcess();
664
665 if (!started)
666 return;
667
668 if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
669 ((PeriodicEventListener) getServlet()).periodicEvent();
670 }
671 }
672
673
674 /**
675 * Extract the root cause from a servlet exception.
676 *
677 * @param e The servlet exception
678 */
679 public static Throwable getRootCause(ServletException e) {
680 Throwable rootCause = e;
681 Throwable rootCauseCheck = null;
682 // Extra aggressive rootCause finding
683 int loops = 0;
684 do {
685 loops++;
686 rootCauseCheck = rootCause.getCause();
687 if (rootCauseCheck != null)
688 rootCause = rootCauseCheck;
689 } while (rootCauseCheck != null && (loops < 20));
690 return rootCause;
691 }
692
693
694 /**
695 * Refuse to add a child Container, because Wrappers are the lowest level
696 * of the Container hierarchy.
697 *
698 * @param child Child container to be added
699 */
700 public void addChild(Container child) {
701
702 throw new IllegalStateException
703 (sm.getString("standardWrapper.notChild"));
704
705 }
706
707
708 /**
709 * Add a new servlet initialization parameter for this servlet.
710 *
711 * @param name Name of this initialization parameter to add
712 * @param value Value of this initialization parameter to add
713 */
714 public void addInitParameter(String name, String value) {
715
716 synchronized (parameters) {
717 parameters.put(name, value);
718 }
719 fireContainerEvent("addInitParameter", name);
720
721 }
722
723
724 /**
725 * Add a new listener interested in InstanceEvents.
726 *
727 * @param listener The new listener
728 */
729 public void addInstanceListener(InstanceListener listener) {
730
731 instanceSupport.addInstanceListener(listener);
732
733 }
734
735
736 /**
737 * Add a mapping associated with the Wrapper.
738 *
739 * @param mapping The new wrapper mapping
740 */
741 public void addMapping(String mapping) {
742
743 synchronized (mappings) {
744 mappings.add(mapping);
745 }
746 fireContainerEvent("addMapping", mapping);
747
748 }
749
750
751 /**
752 * Add a new security role reference record to the set of records for
753 * this servlet.
754 *
755 * @param name Role name used within this servlet
756 * @param link Role name used within the web application
757 */
758 public void addSecurityReference(String name, String link) {
759
760 synchronized (references) {
761 references.put(name, link);
762 }
763 fireContainerEvent("addSecurityReference", name);
764
765 }
766
767
768 /**
769 * Return the associated servlet instance.
770 */
771 public Servlet getServlet() {
772 return instance;
773 }
774
775
776 /**
777 * Allocate an initialized instance of this Servlet that is ready to have
778 * its <code>service()</code> method called. If the servlet class does
779 * not implement <code>SingleThreadModel</code>, the (only) initialized
780 * instance may be returned immediately. If the servlet class implements
781 * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
782 * that this instance is not allocated again until it is deallocated by a
783 * call to <code>deallocate()</code>.
784 *
785 * @exception ServletException if the servlet init() method threw
786 * an exception
787 * @exception ServletException if a loading error occurs
788 */
789 public Servlet allocate() throws ServletException {
790
791 // If we are currently unloading this servlet, throw an exception
792 if (unloading)
793 throw new ServletException
794 (sm.getString("standardWrapper.unloading", getName()));
795
796 boolean newInstance = false;
797
798 // If not SingleThreadedModel, return the same instance every time
799 if (!singleThreadModel) {
800
801 // Load and initialize our instance if necessary
802 if (instance == null) {
803 synchronized (this) {
804 if (instance == null) {
805 try {
806 if (log.isDebugEnabled())
807 log.debug("Allocating non-STM instance");
808
809 instance = loadServlet();
810 // For non-STM, increment here to prevent a race
811 // condition with unload. Bug 43683, test case #3
812 if (!singleThreadModel) {
813 newInstance = true;
814 countAllocated.incrementAndGet();
815 }
816 } catch (ServletException e) {
817 throw e;
818 } catch (Throwable e) {
819 throw new ServletException
820 (sm.getString("standardWrapper.allocate"), e);
821 }
822 }
823 }
824 }
825
826 if (!singleThreadModel) {
827 if (log.isTraceEnabled())
828 log.trace(" Returning non-STM instance");
829 // For new instances, count will have been incremented at the
830 // time of creation
831 if (!newInstance) {
832 countAllocated.incrementAndGet();
833 }
834 return (instance);
835 }
836 }
837
838 synchronized (instancePool) {
839
840 while (countAllocated.get() >= nInstances) {
841 // Allocate a new instance if possible, or else wait
842 if (nInstances < maxInstances) {
843 try {
844 instancePool.push(loadServlet());
845 nInstances++;
846 } catch (ServletException e) {
847 throw e;
848 } catch (Throwable e) {
849 throw new ServletException
850 (sm.getString("standardWrapper.allocate"), e);
851 }
852 } else {
853 try {
854 instancePool.wait();
855 } catch (InterruptedException e) {
856 ;
857 }
858 }
859 }
860 if (log.isTraceEnabled())
861 log.trace(" Returning allocated STM instance");
862 countAllocated.incrementAndGet();
863 return (Servlet) instancePool.pop();
864
865 }
866
867 }
868
869
870 /**
871 * Return this previously allocated servlet to the pool of available
872 * instances. If this servlet class does not implement SingleThreadModel,
873 * no action is actually required.
874 *
875 * @param servlet The servlet to be returned
876 *
877 * @exception ServletException if a deallocation error occurs
878 */
879 public void deallocate(Servlet servlet) throws ServletException {
880
881 // If not SingleThreadModel, no action is required
882 if (!singleThreadModel) {
883 countAllocated.decrementAndGet();
884 return;
885 }
886
887 // Unlock and free this instance
888 synchronized (instancePool) {
889 countAllocated.decrementAndGet();
890 instancePool.push(servlet);
891 instancePool.notify();
892 }
893
894 }
895
896
897 /**
898 * Return the value for the specified initialization parameter name,
899 * if any; otherwise return <code>null</code>.
900 *
901 * @param name Name of the requested initialization parameter
902 */
903 public String findInitParameter(String name) {
904
905 synchronized (parameters) {
906 return ((String) parameters.get(name));
907 }
908
909 }
910
911
912 /**
913 * Return the names of all defined initialization parameters for this
914 * servlet.
915 */
916 public String[] findInitParameters() {
917
918 synchronized (parameters) {
919 String results[] = new String[parameters.size()];
920 return ((String[]) parameters.keySet().toArray(results));
921 }
922
923 }
924
925
926 /**
927 * Return the mappings associated with this wrapper.
928 */
929 public String[] findMappings() {
930
931 synchronized (mappings) {
932 return (String[]) mappings.toArray(new String[mappings.size()]);
933 }
934
935 }
936
937
938 /**
939 * Return the security role link for the specified security role
940 * reference name, if any; otherwise return <code>null</code>.
941 *
942 * @param name Security role reference used within this servlet
943 */
944 public String findSecurityReference(String name) {
945
946 synchronized (references) {
947 return ((String) references.get(name));
948 }
949
950 }
951
952
953 /**
954 * Return the set of security role reference names associated with
955 * this servlet, if any; otherwise return a zero-length array.
956 */
957 public String[] findSecurityReferences() {
958
959 synchronized (references) {
960 String results[] = new String[references.size()];
961 return ((String[]) references.keySet().toArray(results));
962 }
963
964 }
965
966
967 /**
968 * FIXME: Fooling introspection ...
969 */
970 public Wrapper findMappingObject() {
971 return (Wrapper) getMappingObject();
972 }
973
974
975 /**
976 * Load and initialize an instance of this servlet, if there is not already
977 * at least one initialized instance. This can be used, for example, to
978 * load servlets that are marked in the deployment descriptor to be loaded
979 * at server startup time.
980 * <p>
981 * <b>IMPLEMENTATION NOTE</b>: Servlets whose classnames begin with
982 * <code>org.apache.catalina.</code> (so-called "container" servlets)
983 * are loaded by the same classloader that loaded this class, rather than
984 * the classloader for the current web application.
985 * This gives such classes access to Catalina internals, which are
986 * prevented for classes loaded for web applications.
987 *
988 * @exception ServletException if the servlet init() method threw
989 * an exception
990 * @exception ServletException if some other loading problem occurs
991 */
992 public synchronized void load() throws ServletException {
993 instance = loadServlet();
994 }
995
996
997 /**
998 * Load and initialize an instance of this servlet, if there is not already
999 * at least one initialized instance. This can be used, for example, to
1000 * load servlets that are marked in the deployment descriptor to be loaded
1001 * at server startup time.
1002 */
1003 public synchronized Servlet loadServlet() throws ServletException {
1004
1005 // Nothing to do if we already have an instance or an instance pool
1006 if (!singleThreadModel && (instance != null))
1007 return instance;
1008
1009 PrintStream out = System.out;
1010 if (swallowOutput) {
1011 SystemLogHandler.startCapture();
1012 }
1013
1014 Servlet servlet;
1015 try {
1016 long t1=System.currentTimeMillis();
1017 // If this "servlet" is really a JSP file, get the right class.
1018 // HOLD YOUR NOSE - this is a kludge that avoids having to do special
1019 // case Catalina-specific code in Jasper - it also requires that the
1020 // servlet path be replaced by the <jsp-file> element content in
1021 // order to be completely effective
1022 String actualClass = servletClass;
1023 if ((actualClass == null) && (jspFile != null)) {
1024 Wrapper jspWrapper = (Wrapper)
1025 ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
1026 if (jspWrapper != null) {
1027 actualClass = jspWrapper.getServletClass();
1028 // Merge init parameters
1029 String paramNames[] = jspWrapper.findInitParameters();
1030 for (int i = 0; i < paramNames.length; i++) {
1031 if (parameters.get(paramNames[i]) == null) {
1032 parameters.put
1033 (paramNames[i],
1034 jspWrapper.findInitParameter(paramNames[i]));
1035 }
1036 }
1037 }
1038 }
1039
1040 // Complain if no servlet class has been specified
1041 if (actualClass == null) {
1042 unavailable(null);
1043 throw new ServletException
1044 (sm.getString("standardWrapper.notClass", getName()));
1045 }
1046
1047 // Acquire an instance of the class loader to be used
1048 Loader loader = getLoader();
1049 if (loader == null) {
1050 unavailable(null);
1051 throw new ServletException
1052 (sm.getString("standardWrapper.missingLoader", getName()));
1053 }
1054
1055 ClassLoader classLoader = loader.getClassLoader();
1056
1057 // Special case class loader for a container provided servlet
1058 //
1059 if (isContainerProvidedServlet(actualClass) &&
1060 ! ((Context)getParent()).getPrivileged() ) {
1061 // If it is a priviledged context - using its own
1062 // class loader will work, since it's a child of the container
1063 // loader
1064 classLoader = this.getClass().getClassLoader();
1065 }
1066
1067 // Load the specified servlet class from the appropriate class loader
1068 Class classClass = null;
1069 try {
1070 if (SecurityUtil.isPackageProtectionEnabled()){
1071 final ClassLoader fclassLoader = classLoader;
1072 final String factualClass = actualClass;
1073 try{
1074 classClass = (Class)AccessController.doPrivileged(
1075 new PrivilegedExceptionAction(){
1076 public Object run() throws Exception{
1077 if (fclassLoader != null) {
1078 return fclassLoader.loadClass(factualClass);
1079 } else {
1080 return Class.forName(factualClass);
1081 }
1082 }
1083 });
1084 } catch(PrivilegedActionException pax){
1085 Exception ex = pax.getException();
1086 if (ex instanceof ClassNotFoundException){
1087 throw (ClassNotFoundException)ex;
1088 } else {
1089 getServletContext().log( "Error loading "
1090 + fclassLoader + " " + factualClass, ex );
1091 }
1092 }
1093 } else {
1094 if (classLoader != null) {
1095 classClass = classLoader.loadClass(actualClass);
1096 } else {
1097 classClass = Class.forName(actualClass);
1098 }
1099 }
1100 } catch (ClassNotFoundException e) {
1101 unavailable(null);
1102 getServletContext().log( "Error loading " + classLoader + " " + actualClass, e );
1103 throw new ServletException
1104 (sm.getString("standardWrapper.missingClass", actualClass),
1105 e);
1106 }
1107
1108 if (classClass == null) {
1109 unavailable(null);
1110 throw new ServletException
1111 (sm.getString("standardWrapper.missingClass", actualClass));
1112 }
1113
1114 // Instantiate and initialize an instance of the servlet class itself
1115 try {
1116 servlet = (Servlet) classClass.newInstance();
1117 // Annotation processing
1118 if (!((Context) getParent()).getIgnoreAnnotations()) {
1119 if (getParent() instanceof StandardContext) {
1120 ((StandardContext)getParent()).getAnnotationProcessor().processAnnotations(servlet);
1121 ((StandardContext)getParent()).getAnnotationProcessor().postConstruct(servlet);
1122 }
1123 }
1124 } catch (ClassCastException e) {
1125 unavailable(null);
1126 // Restore the context ClassLoader
1127 throw new ServletException
1128 (sm.getString("standardWrapper.notServlet", actualClass), e);
1129 } catch (Throwable e) {
1130 unavailable(null);
1131
1132 // Added extra log statement for Bugzilla 36630:
1133 // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
1134 if(log.isDebugEnabled()) {
1135 log.debug(sm.getString("standardWrapper.instantiate", actualClass), e);
1136 }
1137
1138 // Restore the context ClassLoader
1139 throw new ServletException
1140 (sm.getString("standardWrapper.instantiate", actualClass), e);
1141 }
1142
1143 // Check if loading the servlet in this web application should be
1144 // allowed
1145 if (!isServletAllowed(servlet)) {
1146 throw new SecurityException
1147 (sm.getString("standardWrapper.privilegedServlet",
1148 actualClass));
1149 }
1150
1151 // Special handling for ContainerServlet instances
1152 if ((servlet instanceof ContainerServlet) &&
1153 (isContainerProvidedServlet(actualClass) ||
1154 ((Context)getParent()).getPrivileged() )) {
1155 ((ContainerServlet) servlet).setWrapper(this);
1156 }
1157
1158 classLoadTime=(int) (System.currentTimeMillis() -t1);
1159 // Call the initialization method of this servlet
1160 try {
1161 instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
1162 servlet);
1163
1164 if( Globals.IS_SECURITY_ENABLED) {
1165
1166 Object[] args = new Object[]{((ServletConfig)facade)};
1167 SecurityUtil.doAsPrivilege("init",
1168 servlet,
1169 classType,
1170 args);
1171 args = null;
1172 } else {
1173 servlet.init(facade);
1174 }
1175
1176 // Invoke jspInit on JSP pages
1177 if ((loadOnStartup >= 0) && (jspFile != null)) {
1178 // Invoking jspInit
1179 DummyRequest req = new DummyRequest();
1180 req.setServletPath(jspFile);
1181 req.setQueryString(Constants.PRECOMPILE + "=true");
1182 DummyResponse res = new DummyResponse();
1183
1184 if( Globals.IS_SECURITY_ENABLED) {
1185 Object[] args = new Object[]{req, res};
1186 SecurityUtil.doAsPrivilege("service",
1187 servlet,
1188 classTypeUsedInService,
1189 args);
1190 args = null;
1191 } else {
1192 servlet.service(req, res);
1193 }
1194 }
1195 instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
1196 servlet);
1197 } catch (UnavailableException f) {
1198 instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
1199 servlet, f);
1200 unavailable(f);
1201 throw f;
1202 } catch (ServletException f) {
1203 instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
1204 servlet, f);
1205 // If the servlet wanted to be unavailable it would have
1206 // said so, so do not call unavailable(null).
1207 throw f;
1208 } catch (Throwable f) {
1209 getServletContext().log("StandardWrapper.Throwable", f );
1210 instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
1211 servlet, f);
1212 // If the servlet wanted to be unavailable it would have
1213 // said so, so do not call unavailable(null).
1214 throw new ServletException
1215 (sm.getString("standardWrapper.initException", getName()), f);
1216 }
1217
1218 // Register our newly initialized instance
1219 singleThreadModel = servlet instanceof SingleThreadModel;
1220 if (singleThreadModel) {
1221 if (instancePool == null)
1222 instancePool = new Stack();
1223 }
1224 fireContainerEvent("load", this);
1225
1226 loadTime=System.currentTimeMillis() -t1;
1227 } finally {
1228 if (swallowOutput) {
1229 String log = SystemLogHandler.stopCapture();
1230 if (log != null && log.length() > 0) {
1231 if (getServletContext() != null) {
1232 getServletContext().log(log);
1233 } else {
1234 out.println(log);
1235 }
1236 }
1237 }
1238 }
1239 return servlet;
1240
1241 }
1242
1243
1244 /**
1245 * Remove the specified initialization parameter from this servlet.
1246 *
1247 * @param name Name of the initialization parameter to remove
1248 */
1249 public void removeInitParameter(String name) {
1250
1251 synchronized (parameters) {
1252 parameters.remove(name);
1253 }
1254 fireContainerEvent("removeInitParameter", name);
1255
1256 }
1257
1258
1259 /**
1260 * Remove a listener no longer interested in InstanceEvents.
1261 *
1262 * @param listener The listener to remove
1263 */
1264 public void removeInstanceListener(InstanceListener listener) {
1265
1266 instanceSupport.removeInstanceListener(listener);
1267
1268 }
1269
1270
1271 /**
1272 * Remove a mapping associated with the wrapper.
1273 *
1274 * @param mapping The pattern to remove
1275 */
1276 public void removeMapping(String mapping) {
1277
1278 synchronized (mappings) {
1279 mappings.remove(mapping);
1280 }
1281 fireContainerEvent("removeMapping", mapping);
1282
1283 }
1284
1285
1286 /**
1287 * Remove any security role reference for the specified role name.
1288 *
1289 * @param name Security role used within this servlet to be removed
1290 */
1291 public void removeSecurityReference(String name) {
1292
1293 synchronized (references) {
1294 references.remove(name);
1295 }
1296 fireContainerEvent("removeSecurityReference", name);
1297
1298 }
1299
1300
1301 /**
1302 * Return a String representation of this component.
1303 */
1304 public String toString() {
1305
1306 StringBuffer sb = new StringBuffer();
1307 if (getParent() != null) {
1308 sb.append(getParent().toString());
1309 sb.append(".");
1310 }
1311 sb.append("StandardWrapper[");
1312 sb.append(getName());
1313 sb.append("]");
1314 return (sb.toString());
1315
1316 }
1317
1318
1319 /**
1320 * Process an UnavailableException, marking this servlet as unavailable
1321 * for the specified amount of time.
1322 *
1323 * @param unavailable The exception that occurred, or <code>null</code>
1324 * to mark this servlet as permanently unavailable
1325 */
1326 public void unavailable(UnavailableException unavailable) {
1327 getServletContext().log(sm.getString("standardWrapper.unavailable", getName()));
1328 if (unavailable == null)
1329 setAvailable(Long.MAX_VALUE);
1330 else if (unavailable.isPermanent())
1331 setAvailable(Long.MAX_VALUE);
1332 else {
1333 int unavailableSeconds = unavailable.getUnavailableSeconds();
1334 if (unavailableSeconds <= 0)
1335 unavailableSeconds = 60; // Arbitrary default
1336 setAvailable(System.currentTimeMillis() +
1337 (unavailableSeconds * 1000L));
1338 }
1339
1340 }
1341
1342
1343 /**
1344 * Unload all initialized instances of this servlet, after calling the
1345 * <code>destroy()</code> method for each instance. This can be used,
1346 * for example, prior to shutting down the entire servlet engine, or
1347 * prior to reloading all of the classes from the Loader associated with
1348 * our Loader's repository.
1349 *
1350 * @exception ServletException if an exception is thrown by the
1351 * destroy() method
1352 */
1353 public synchronized void unload() throws ServletException {
1354
1355 // Nothing to do if we have never loaded the instance
1356 if (!singleThreadModel && (instance == null))
1357 return;
1358 unloading = true;
1359
1360 // Loaf a while if the current instance is allocated
1361 // (possibly more than once if non-STM)
1362 if (countAllocated.get() > 0) {
1363 int nRetries = 0;
1364 long delay = unloadDelay / 20;
1365 while ((nRetries < 21) && (countAllocated.get() > 0)) {
1366 if ((nRetries % 10) == 0) {
1367 log.info(sm.getString("standardWrapper.waiting",
1368 countAllocated.toString()));
1369 }
1370 try {
1371 Thread.sleep(delay);
1372 } catch (InterruptedException e) {
1373 ;
1374 }
1375 nRetries++;
1376 }
1377 }
1378
1379 PrintStream out = System.out;
1380 if (swallowOutput) {
1381 SystemLogHandler.startCapture();
1382 }
1383
1384 // Call the servlet destroy() method
1385 try {
1386 instanceSupport.fireInstanceEvent
1387 (InstanceEvent.BEFORE_DESTROY_EVENT, instance);
1388
1389 if( Globals.IS_SECURITY_ENABLED) {
1390 SecurityUtil.doAsPrivilege("destroy",
1391 instance);
1392 SecurityUtil.remove(instance);
1393 } else {
1394 instance.destroy();
1395 }
1396
1397 instanceSupport.fireInstanceEvent
1398 (InstanceEvent.AFTER_DESTROY_EVENT, instance);
1399
1400 // Annotation processing
1401 if (!((Context) getParent()).getIgnoreAnnotations()) {
1402 ((StandardContext)getParent()).getAnnotationProcessor().preDestroy(instance);
1403 }
1404
1405 } catch (Throwable t) {
1406 instanceSupport.fireInstanceEvent
1407 (InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
1408 instance = null;
1409 instancePool = null;
1410 nInstances = 0;
1411 fireContainerEvent("unload", this);
1412 unloading = false;
1413 throw new ServletException
1414 (sm.getString("standardWrapper.destroyException", getName()),
1415 t);
1416 } finally {
1417 // Write captured output
1418 if (swallowOutput) {
1419 String log = SystemLogHandler.stopCapture();
1420 if (log != null && log.length() > 0) {
1421 if (getServletContext() != null) {
1422 getServletContext().log(log);
1423 } else {
1424 out.println(log);
1425 }
1426 }
1427 }
1428 }
1429
1430 // Deregister the destroyed instance
1431 instance = null;
1432
1433 if (singleThreadModel && (instancePool != null)) {
1434 try {
1435 while (!instancePool.isEmpty()) {
1436 Servlet s = (Servlet) instancePool.pop();
1437 if (Globals.IS_SECURITY_ENABLED) {
1438 SecurityUtil.doAsPrivilege("destroy", s);
1439 SecurityUtil.remove(instance);
1440 } else {
1441 s.destroy();
1442 }
1443 // Annotation processing
1444 if (!((Context) getParent()).getIgnoreAnnotations()) {
1445 ((StandardContext)getParent()).getAnnotationProcessor().preDestroy(s);
1446 }
1447 }
1448 } catch (Throwable t) {
1449 instancePool = null;
1450 nInstances = 0;
1451 unloading = false;
1452 fireContainerEvent("unload", this);
1453 throw new ServletException
1454 (sm.getString("standardWrapper.destroyException",
1455 getName()), t);
1456 }
1457 instancePool = null;
1458 nInstances = 0;
1459 }
1460
1461 singleThreadModel = false;
1462
1463 unloading = false;
1464 fireContainerEvent("unload", this);
1465
1466 }
1467
1468
1469 // -------------------------------------------------- ServletConfig Methods
1470
1471
1472 /**
1473 * Return the initialization parameter value for the specified name,
1474 * if any; otherwise return <code>null</code>.
1475 *
1476 * @param name Name of the initialization parameter to retrieve
1477 */
1478 public String getInitParameter(String name) {
1479
1480 return (findInitParameter(name));
1481
1482 }
1483
1484
1485 /**
1486 * Return the set of initialization parameter names defined for this
1487 * servlet. If none are defined, an empty Enumeration is returned.
1488 */
1489 public Enumeration getInitParameterNames() {
1490
1491 synchronized (parameters) {
1492 return (new Enumerator(parameters.keySet()));
1493 }
1494
1495 }
1496
1497
1498 /**
1499 * Return the servlet context with which this servlet is associated.
1500 */
1501 public ServletContext getServletContext() {
1502
1503 if (parent == null)
1504 return (null);
1505 else if (!(parent instanceof Context))
1506 return (null);
1507 else
1508 return (((Context) parent).getServletContext());
1509
1510 }
1511
1512
1513 /**
1514 * Return the name of this servlet.
1515 */
1516 public String getServletName() {
1517
1518 return (getName());
1519
1520 }
1521
1522 public long getProcessingTime() {
1523 return swValve.getProcessingTime();
1524 }
1525
1526 public void setProcessingTime(long processingTime) {
1527 swValve.setProcessingTime(processingTime);
1528 }
1529
1530 public long getMaxTime() {
1531 return swValve.getMaxTime();
1532 }
1533
1534 public void setMaxTime(long maxTime) {
1535 swValve.setMaxTime(maxTime);
1536 }
1537
1538 public long getMinTime() {
1539 return swValve.getMinTime();
1540 }
1541
1542 public void setMinTime(long minTime) {
1543 swValve.setMinTime(minTime);
1544 }
1545
1546 public int getRequestCount() {
1547 return swValve.getRequestCount();
1548 }
1549
1550 public void setRequestCount(int requestCount) {
1551 swValve.setRequestCount(requestCount);
1552 }
1553
1554 public int getErrorCount() {
1555 return swValve.getErrorCount();
1556 }
1557
1558 public void setErrorCount(int errorCount) {
1559 swValve.setErrorCount(errorCount);
1560 }
1561
1562 /**
1563 * Increment the error count used for monitoring.
1564 */
1565 public void incrementErrorCount(){
1566 swValve.setErrorCount(swValve.getErrorCount() + 1);
1567 }
1568
1569 public long getLoadTime() {
1570 return loadTime;
1571 }
1572
1573 public void setLoadTime(long loadTime) {
1574 this.loadTime = loadTime;
1575 }
1576
1577 public int getClassLoadTime() {
1578 return classLoadTime;
1579 }
1580
1581 // -------------------------------------------------------- Package Methods
1582
1583
1584 // -------------------------------------------------------- protected Methods
1585
1586
1587 /**
1588 * Add a default Mapper implementation if none have been configured
1589 * explicitly.
1590 *
1591 * @param mapperClass Java class name of the default Mapper
1592 */
1593 protected void addDefaultMapper(String mapperClass) {
1594
1595 ; // No need for a default Mapper on a Wrapper
1596
1597 }
1598
1599
1600 /**
1601 * Return <code>true</code> if the specified class name represents a
1602 * container provided servlet class that should be loaded by the
1603 * server class loader.
1604 *
1605 * @param classname Name of the class to be checked
1606 */
1607 protected boolean isContainerProvidedServlet(String classname) {
1608
1609 if (classname.startsWith("org.apache.catalina.")) {
1610 return (true);
1611 }
1612 try {
1613 Class clazz =
1614 this.getClass().getClassLoader().loadClass(classname);
1615 return (ContainerServlet.class.isAssignableFrom(clazz));
1616 } catch (Throwable t) {
1617 return (false);
1618 }
1619
1620 }
1621
1622
1623 /**
1624 * Return <code>true</code> if loading this servlet is allowed.
1625 */
1626 protected boolean isServletAllowed(Object servlet) {
1627
1628 // Privileged webapps may load all servlets without restriction
1629 if (((Context) getParent()).getPrivileged()) {
1630 return true;
1631 }
1632
1633 if (servlet instanceof ContainerServlet) {
1634 return (false);
1635 }
1636
1637 Class clazz = servlet.getClass();
1638 while (clazz != null && !clazz.getName().equals("javax.servlet.http.HttpServlet")) {
1639 if ("restricted".equals(restrictedServlets.getProperty(clazz.getName()))) {
1640 return (false);
1641 }
1642 clazz = clazz.getSuperclass();
1643 }
1644
1645 return (true);
1646
1647 }
1648
1649
1650 protected Method[] getAllDeclaredMethods(Class c) {
1651
1652 if (c.equals(javax.servlet.http.HttpServlet.class)) {
1653 return null;
1654 }
1655
1656 Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
1657
1658 Method[] thisMethods = c.getDeclaredMethods();
1659 if (thisMethods == null) {
1660 return parentMethods;
1661 }
1662
1663 if ((parentMethods != null) && (parentMethods.length > 0)) {
1664 Method[] allMethods =
1665 new Method[parentMethods.length + thisMethods.length];
1666 System.arraycopy(parentMethods, 0, allMethods, 0,
1667 parentMethods.length);
1668 System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
1669 thisMethods.length);
1670
1671 thisMethods = allMethods;
1672 }
1673
1674 return thisMethods;
1675 }
1676
1677
1678 // ------------------------------------------------------ Lifecycle Methods
1679
1680
1681 /**
1682 * Start this component, pre-loading the servlet if the load-on-startup
1683 * value is set appropriately.
1684 *
1685 * @exception LifecycleException if a fatal error occurs during startup
1686 */
1687 public void start() throws LifecycleException {
1688
1689 // Send j2ee.state.starting notification
1690 if (this.getObjectName() != null) {
1691 Notification notification = new Notification("j2ee.state.starting",
1692 this.getObjectName(),
1693 sequenceNumber++);
1694 broadcaster.sendNotification(notification);
1695 }
1696
1697 // Start up this component
1698 super.start();
1699
1700 if( oname != null )
1701 registerJMX((StandardContext)getParent());
1702
1703 // Load and initialize an instance of this servlet if requested
1704 // MOVED TO StandardContext START() METHOD
1705
1706 setAvailable(0L);
1707
1708 // Send j2ee.state.running notification
1709 if (this.getObjectName() != null) {
1710 Notification notification =
1711 new Notification("j2ee.state.running", this.getObjectName(),
1712 sequenceNumber++);
1713 broadcaster.sendNotification(notification);
1714 }
1715
1716 }
1717
1718
1719 /**
1720 * Stop this component, gracefully shutting down the servlet if it has
1721 * been initialized.
1722 *
1723 * @exception LifecycleException if a fatal error occurs during shutdown
1724 */
1725 public void stop() throws LifecycleException {
1726
1727 setAvailable(Long.MAX_VALUE);
1728
1729 // Send j2ee.state.stopping notification
1730 if (this.getObjectName() != null) {
1731 Notification notification =
1732 new Notification("j2ee.state.stopping", this.getObjectName(),
1733 sequenceNumber++);
1734 broadcaster.sendNotification(notification);
1735 }
1736
1737 // Shut down our servlet instance (if it has been initialized)
1738 try {
1739 unload();
1740 } catch (ServletException e) {
1741 getServletContext().log(sm.getString
1742 ("standardWrapper.unloadException", getName()), e);
1743 }
1744
1745 // Shut down this component
1746 super.stop();
1747
1748 // Send j2ee.state.stoppped notification
1749 if (this.getObjectName() != null) {
1750 Notification notification =
1751 new Notification("j2ee.state.stopped", this.getObjectName(),
1752 sequenceNumber++);
1753 broadcaster.sendNotification(notification);
1754 }
1755
1756 if( oname != null ) {
1757 Registry.getRegistry(null, null).unregisterComponent(oname);
1758
1759 // Send j2ee.object.deleted notification
1760 Notification notification =
1761 new Notification("j2ee.object.deleted", this.getObjectName(),
1762 sequenceNumber++);
1763 broadcaster.sendNotification(notification);
1764 }
1765
1766 if (isJspServlet && jspMonitorON != null ) {
1767 Registry.getRegistry(null, null).unregisterComponent(jspMonitorON);
1768 }
1769
1770 }
1771
1772 protected void registerJMX(StandardContext ctx) {
1773
1774 String parentName = ctx.getName();
1775 parentName = ("".equals(parentName)) ? "/" : parentName;
1776
1777 String hostName = ctx.getParent().getName();
1778 hostName = (hostName==null) ? "DEFAULT" : hostName;
1779
1780 String domain = ctx.getDomain();
1781
1782 String webMod= "//" + hostName + parentName;
1783 String onameStr = domain + ":j2eeType=Servlet,name=" + getName() +
1784 ",WebModule=" + webMod + ",J2EEApplication=" +
1785 ctx.getJ2EEApplication() + ",J2EEServer=" +
1786 ctx.getJ2EEServer();
1787 try {
1788 oname=new ObjectName(onameStr);
1789 controller=oname;
1790 Registry.getRegistry(null, null)
1791 .registerComponent(this, oname, null );
1792
1793 // Send j2ee.object.created notification
1794 if (this.getObjectName() != null) {
1795 Notification notification = new Notification(
1796 "j2ee.object.created",
1797 this.getObjectName(),
1798 sequenceNumber++);
1799 broadcaster.sendNotification(notification);
1800 }
1801 } catch( Exception ex ) {
1802 log.info("Error registering servlet with jmx " + this, ex);
1803 }
1804
1805 if (isJspServlet) {
1806 // Register JSP monitoring mbean
1807 onameStr = domain + ":type=JspMonitor,name=" + getName()
1808 + ",WebModule=" + webMod
1809 + ",J2EEApplication=" + ctx.getJ2EEApplication()
1810 + ",J2EEServer=" + ctx.getJ2EEServer();
1811 try {
1812 jspMonitorON = new ObjectName(onameStr);
1813 Registry.getRegistry(null, null)
1814 .registerComponent(instance, jspMonitorON, null);
1815 } catch( Exception ex ) {
1816 log.info("Error registering JSP monitoring with jmx " +
1817 instance, ex);
1818 }
1819 }
1820 }
1821
1822
1823 /* Remove a JMX notficationListener
1824 * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
1825 */
1826 public void removeNotificationListener(NotificationListener listener,
1827 NotificationFilter filter, Object object) throws ListenerNotFoundException {
1828 broadcaster.removeNotificationListener(listener,filter,object);
1829
1830 }
1831
1832 protected MBeanNotificationInfo[] notificationInfo;
1833
1834 /* Get JMX Broadcaster Info
1835 * @TODO use StringManager for international support!
1836 * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
1837 * @see javax.management.NotificationBroadcaster#getNotificationInfo()
1838 */
1839 public MBeanNotificationInfo[] getNotificationInfo() {
1840
1841 if(notificationInfo == null) {
1842 notificationInfo = new MBeanNotificationInfo[]{
1843 new MBeanNotificationInfo(new String[] {
1844 "j2ee.object.created"},
1845 Notification.class.getName(),
1846 "servlet is created"
1847 ),
1848 new MBeanNotificationInfo(new String[] {
1849 "j2ee.state.starting"},
1850 Notification.class.getName(),
1851 "servlet is starting"
1852 ),
1853 new MBeanNotificationInfo(new String[] {
1854 "j2ee.state.running"},
1855 Notification.class.getName(),
1856 "servlet is running"
1857 ),
1858 new MBeanNotificationInfo(new String[] {
1859 "j2ee.state.stopped"},
1860 Notification.class.getName(),
1861 "servlet start to stopped"
1862 ),
1863 new MBeanNotificationInfo(new String[] {
1864 "j2ee.object.stopped"},
1865 Notification.class.getName(),
1866 "servlet is stopped"
1867 ),
1868 new MBeanNotificationInfo(new String[] {
1869 "j2ee.object.deleted"},
1870 Notification.class.getName(),
1871 "servlet is deleted"
1872 )
1873 };
1874
1875 }
1876
1877 return notificationInfo;
1878 }
1879
1880
1881 /* Add a JMX-NotificationListener
1882 * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
1883 */
1884 public void addNotificationListener(NotificationListener listener,
1885 NotificationFilter filter, Object object) throws IllegalArgumentException {
1886 broadcaster.addNotificationListener(listener,filter,object);
1887 }
1888
1889
1890 /**
1891 * Remove a JMX-NotificationListener
1892 * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
1893 */
1894 public void removeNotificationListener(NotificationListener listener)
1895 throws ListenerNotFoundException {
1896 broadcaster.removeNotificationListener(listener);
1897 }
1898
1899
1900 // ------------------------------------------------------------- Attributes
1901
1902
1903 public boolean isEventProvider() {
1904 return false;
1905 }
1906
1907 public boolean isStateManageable() {
1908 return false;
1909 }
1910
1911 public boolean isStatisticsProvider() {
1912 return false;
1913 }
1914
1915
1916 }