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