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
247
248
249 @ManagedAttribute(value="forced servlet path", readonly=true)
250 public String getForcedPath()
251 {
252 return _forcedPath;
253 }
254
255
256
257
258
259 public void setForcedPath(String forcedPath)
260 {
261 _forcedPath = forcedPath;
262 }
263
264 public boolean isEnabled()
265 {
266 return _enabled;
267 }
268
269
270 public void setEnabled(boolean enabled)
271 {
272 _enabled = enabled;
273 }
274
275
276
277 public void doStart()
278 throws Exception
279 {
280
281
282 _unavailable=0;
283 if (!_enabled)
284 return;
285
286
287 if (_forcedPath != null)
288 {
289
290 String precompiled="org.apache.jsp"+_forcedPath.replace('.','_').replace('/','.');
291
292 ServletHolder jsp=getServletHandler().getServlet(precompiled);
293 if (jsp!=null)
294 {
295 LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
296
297 setClassName(jsp.getClassName());
298 }
299 else
300 {
301
302 jsp=getServletHandler().getServlet("jsp");
303 if (jsp!=null)
304 {
305 LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
306 setClassName(jsp.getClassName());
307 }
308 }
309 }
310
311
312
313 try
314 {
315 super.doStart();
316 }
317 catch (UnavailableException ue)
318 {
319 makeUnavailable(ue);
320 if (_servletHandler.isStartWithUnavailable())
321 {
322 LOG.ignore(ue);
323 return;
324 }
325 else
326 throw ue;
327 }
328
329
330
331 try
332 {
333 checkServletType();
334 }
335 catch (UnavailableException ue)
336 {
337 makeUnavailable(ue);
338 if (_servletHandler.isStartWithUnavailable())
339 {
340 LOG.ignore(ue);
341 return;
342 }
343 else
344 throw ue;
345 }
346
347
348 checkInitOnStartup();
349
350 _identityService = _servletHandler.getIdentityService();
351 if (_identityService!=null && _runAsRole!=null)
352 _runAsToken=_identityService.newRunAsToken(_runAsRole);
353
354 _config=new Config();
355
356 if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
357 _servlet = new SingleThreadedWrapper();
358
359 }
360
361
362
363 @Override
364 public void initialize ()
365 throws Exception
366 {
367 super.initialize();
368 if (_extInstance || _initOnStartup)
369 {
370 try
371 {
372 initServlet();
373 }
374 catch(Exception e)
375 {
376 if (_servletHandler.isStartWithUnavailable())
377 LOG.ignore(e);
378 else
379 throw e;
380 }
381 }
382 }
383
384
385
386 public void doStop()
387 throws Exception
388 {
389 Object old_run_as = null;
390 if (_servlet!=null)
391 {
392 try
393 {
394 if (_identityService!=null)
395 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
396
397 destroyInstance(_servlet);
398 }
399 catch (Exception e)
400 {
401 LOG.warn(e);
402 }
403 finally
404 {
405 if (_identityService!=null)
406 _identityService.unsetRunAs(old_run_as);
407 }
408 }
409
410 if (!_extInstance)
411 _servlet=null;
412
413 _config=null;
414 }
415
416
417 @Override
418 public void destroyInstance (Object o)
419 throws Exception
420 {
421 if (o==null)
422 return;
423 Servlet servlet = ((Servlet)o);
424 getServletHandler().destroyServlet(servlet);
425 servlet.destroy();
426 }
427
428
429
430
431
432 public synchronized Servlet getServlet()
433 throws ServletException
434 {
435
436 if (_unavailable!=0)
437 {
438 if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
439 throw _unavailableEx;
440 _unavailable=0;
441 _unavailableEx=null;
442 }
443
444 if (_servlet==null)
445 initServlet();
446 return _servlet;
447 }
448
449
450
451
452
453 public Servlet getServletInstance()
454 {
455 return _servlet;
456 }
457
458
459
460
461
462
463 public void checkServletType ()
464 throws UnavailableException
465 {
466 if (_class==null || !javax.servlet.Servlet.class.isAssignableFrom(_class))
467 {
468 throw new UnavailableException("Servlet "+_class+" is not a javax.servlet.Servlet");
469 }
470 }
471
472
473
474
475
476 public boolean isAvailable()
477 {
478 if (isStarted()&& _unavailable==0)
479 return true;
480 try
481 {
482 getServlet();
483 }
484 catch(Exception e)
485 {
486 LOG.ignore(e);
487 }
488
489 return isStarted()&& _unavailable==0;
490 }
491
492
493
494
495
496
497
498
499
500 private void checkInitOnStartup()
501 {
502 if (_class==null)
503 return;
504
505 if ((_class.getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup)
506 setInitOrder(Integer.MAX_VALUE);
507 }
508
509
510 private void makeUnavailable(UnavailableException e)
511 {
512 if (_unavailableEx==e && _unavailable!=0)
513 return;
514
515 _servletHandler.getServletContext().log("unavailable",e);
516
517 _unavailableEx=e;
518 _unavailable=-1;
519 if (e.isPermanent())
520 _unavailable=-1;
521 else
522 {
523 if (_unavailableEx.getUnavailableSeconds()>0)
524 _unavailable=System.currentTimeMillis()+1000*_unavailableEx.getUnavailableSeconds();
525 else
526 _unavailable=System.currentTimeMillis()+5000;
527 }
528 }
529
530
531
532 private void makeUnavailable(final Throwable e)
533 {
534 if (e instanceof UnavailableException)
535 makeUnavailable((UnavailableException)e);
536 else
537 {
538 ServletContext ctx = _servletHandler.getServletContext();
539 if (ctx==null)
540 LOG.info("unavailable",e);
541 else
542 ctx.log("unavailable",e);
543 _unavailableEx=new UnavailableException(String.valueOf(e),-1)
544 {
545 {
546 initCause(e);
547 }
548 };
549 _unavailable=-1;
550 }
551 }
552
553
554 private void initServlet()
555 throws ServletException
556 {
557 Object old_run_as = null;
558 try
559 {
560 if (_servlet==null)
561 _servlet=newInstance();
562 if (_config==null)
563 _config=new Config();
564
565
566
567
568 if (_identityService!=null)
569 {
570 old_run_as=_identityService.setRunAs(_identityService.getSystemUserIdentity(),_runAsToken);
571 }
572
573
574 if (isJspServlet())
575 {
576 initJspServlet();
577 }
578
579 initMultiPart();
580
581 LOG.debug("Filter.init {}",_servlet);
582 _servlet.init(_config);
583 }
584 catch (UnavailableException e)
585 {
586 makeUnavailable(e);
587 _servlet=null;
588 _config=null;
589 throw e;
590 }
591 catch (ServletException e)
592 {
593 makeUnavailable(e.getCause()==null?e:e.getCause());
594 _servlet=null;
595 _config=null;
596 throw e;
597 }
598 catch (Exception e)
599 {
600 makeUnavailable(e);
601 _servlet=null;
602 _config=null;
603 throw new ServletException(this.toString(),e);
604 }
605 finally
606 {
607
608 if (_identityService!=null)
609 _identityService.unsetRunAs(old_run_as);
610 }
611 }
612
613
614
615
616
617
618 protected void initJspServlet () throws Exception
619 {
620 ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
621
622
623 ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
624
625
626 setInitParameter("com.sun.appserv.jsp.classpath", Loader.getClassPath(ch.getClassLoader().getParent()));
627
628
629 if ("?".equals(getInitParameter("classpath")))
630 {
631 String classpath = ch.getClassPath();
632 LOG.debug("classpath=" + classpath);
633 if (classpath != null)
634 setInitParameter("classpath", classpath);
635 }
636 }
637
638
639
640
641
642
643
644
645 protected void initMultiPart () throws Exception
646 {
647
648
649 if (((Registration)getRegistration()).getMultipartConfig() != null)
650 {
651
652
653
654 ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext());
655 ch.addEventListener(new Request.MultiPartCleanerListener());
656 }
657 }
658
659
660
661
662
663 @Override
664 public String getContextPath()
665 {
666 return _config.getServletContext().getContextPath();
667 }
668
669
670
671
672
673 @Override
674 public Map<String, String> getRoleRefMap()
675 {
676 return _roleMap;
677 }
678
679
680 @ManagedAttribute(value="role to run servlet as", readonly=true)
681 public String getRunAsRole()
682 {
683 return _runAsRole;
684 }
685
686
687 public void setRunAsRole(String role)
688 {
689 _runAsRole = role;
690 }
691
692
693
694
695 public void handle(Request baseRequest,
696 ServletRequest request,
697 ServletResponse response)
698 throws ServletException,
699 UnavailableException,
700 IOException
701 {
702 if (_class==null)
703 throw new UnavailableException("Servlet Not Initialized");
704
705 Servlet servlet=_servlet;
706 synchronized(this)
707 {
708 if (!isStarted())
709 throw new UnavailableException("Servlet not initialized", -1);
710 if (_unavailable!=0 || !_initOnStartup)
711 servlet=getServlet();
712 if (servlet==null)
713 throw new UnavailableException("Could not instantiate "+_class);
714 }
715
716
717 boolean servlet_error=true;
718 Object old_run_as = null;
719 boolean suspendable = baseRequest.isAsyncSupported();
720 try
721 {
722
723 if (_forcedPath!=null)
724
725 request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
726
727
728 if (_identityService!=null)
729 old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
730
731 if (!isAsyncSupported())
732 baseRequest.setAsyncSupported(false);
733
734 MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
735 if (mpce != null)
736 request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
737
738 servlet.service(request,response);
739 servlet_error=false;
740 }
741 catch(UnavailableException e)
742 {
743 makeUnavailable(e);
744 throw _unavailableEx;
745 }
746 finally
747 {
748 baseRequest.setAsyncSupported(suspendable);
749
750
751 if (_identityService!=null)
752 _identityService.unsetRunAs(old_run_as);
753
754
755 if (servlet_error)
756 request.setAttribute("javax.servlet.error.servlet_name",getName());
757 }
758 }
759
760
761
762 private boolean isJspServlet ()
763 {
764 if (_servlet == null)
765 return false;
766
767 Class c = _servlet.getClass();
768
769 boolean result = false;
770 while (c != null && !result)
771 {
772 result = isJspServlet(c.getName());
773 c = c.getSuperclass();
774 }
775
776 return result;
777 }
778
779
780
781 private boolean isJspServlet (String classname)
782 {
783 if (classname == null)
784 return false;
785 return ("org.apache.jasper.servlet.JspServlet".equals(classname));
786 }
787
788
789
790
791
792 protected class Config extends HolderConfig implements ServletConfig
793 {
794
795 @Override
796 public String getServletName()
797 {
798 return getName();
799 }
800
801 }
802
803
804
805
806 public class Registration extends HolderRegistration implements ServletRegistration.Dynamic
807 {
808 protected MultipartConfigElement _multipartConfig;
809
810 @Override
811 public Set<String> addMapping(String... urlPatterns)
812 {
813 illegalStateIfContextStarted();
814 Set<String> clash=null;
815 for (String pattern : urlPatterns)
816 {
817 ServletMapping mapping = _servletHandler.getServletMapping(pattern);
818 if (mapping!=null)
819 {
820
821 if (!mapping.isDefault())
822 {
823 if (clash==null)
824 clash=new HashSet<String>();
825 clash.add(pattern);
826 }
827 }
828 }
829
830
831 if (clash!=null)
832 return clash;
833
834
835 ServletMapping mapping = new ServletMapping();
836 mapping.setServletName(ServletHolder.this.getName());
837 mapping.setPathSpecs(urlPatterns);
838 _servletHandler.addServletMapping(mapping);
839
840 return Collections.emptySet();
841 }
842
843 @Override
844 public Collection<String> getMappings()
845 {
846 ServletMapping[] mappings =_servletHandler.getServletMappings();
847 List<String> patterns=new ArrayList<String>();
848 if (mappings!=null)
849 {
850 for (ServletMapping mapping : mappings)
851 {
852 if (!mapping.getServletName().equals(getName()))
853 continue;
854 String[] specs=mapping.getPathSpecs();
855 if (specs!=null && specs.length>0)
856 patterns.addAll(Arrays.asList(specs));
857 }
858 }
859 return patterns;
860 }
861
862 @Override
863 public String getRunAsRole()
864 {
865 return _runAsRole;
866 }
867
868 @Override
869 public void setLoadOnStartup(int loadOnStartup)
870 {
871 illegalStateIfContextStarted();
872 ServletHolder.this.setInitOrder(loadOnStartup);
873 }
874
875 public int getInitOrder()
876 {
877 return ServletHolder.this.getInitOrder();
878 }
879
880 @Override
881 public void setMultipartConfig(MultipartConfigElement element)
882 {
883 _multipartConfig = element;
884 }
885
886 public MultipartConfigElement getMultipartConfig()
887 {
888 return _multipartConfig;
889 }
890
891 @Override
892 public void setRunAsRole(String role)
893 {
894 _runAsRole = role;
895 }
896
897 @Override
898 public Set<String> setServletSecurity(ServletSecurityElement securityElement)
899 {
900 return _servletHandler.setServletSecurity(this, securityElement);
901 }
902 }
903
904 public ServletRegistration.Dynamic getRegistration()
905 {
906 if (_registration == null)
907 _registration = new Registration();
908 return _registration;
909 }
910
911
912
913
914 private class SingleThreadedWrapper implements Servlet
915 {
916 Stack<Servlet> _stack=new Stack<Servlet>();
917
918 @Override
919 public void destroy()
920 {
921 synchronized(this)
922 {
923 while(_stack.size()>0)
924 try { (_stack.pop()).destroy(); } catch (Exception e) { LOG.warn(e); }
925 }
926 }
927
928 @Override
929 public ServletConfig getServletConfig()
930 {
931 return _config;
932 }
933
934 @Override
935 public String getServletInfo()
936 {
937 return null;
938 }
939
940 @Override
941 public void init(ServletConfig config) throws ServletException
942 {
943 synchronized(this)
944 {
945 if(_stack.size()==0)
946 {
947 try
948 {
949 Servlet s = newInstance();
950 s.init(config);
951 _stack.push(s);
952 }
953 catch (ServletException e)
954 {
955 throw e;
956 }
957 catch (Exception e)
958 {
959 throw new ServletException(e);
960 }
961 }
962 }
963 }
964
965 @Override
966 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
967 {
968 Servlet s;
969 synchronized(this)
970 {
971 if(_stack.size()>0)
972 s=(Servlet)_stack.pop();
973 else
974 {
975 try
976 {
977 s = newInstance();
978 s.init(_config);
979 }
980 catch (ServletException e)
981 {
982 throw e;
983 }
984 catch (Exception e)
985 {
986 throw new ServletException(e);
987 }
988 }
989 }
990
991 try
992 {
993 s.service(req,res);
994 }
995 finally
996 {
997 synchronized(this)
998 {
999 _stack.push(s);
1000 }
1001 }
1002 }
1003 }
1004
1005
1006
1007
1008
1009
1010
1011
1012 protected Servlet newInstance() throws ServletException, IllegalAccessException, InstantiationException
1013 {
1014 try
1015 {
1016 ServletContext ctx = getServletHandler().getServletContext();
1017 if (ctx instanceof ServletContextHandler.Context)
1018 return ((ServletContextHandler.Context)ctx).createServlet(getHeldClass());
1019 return getHeldClass().newInstance();
1020 }
1021 catch (ServletException se)
1022 {
1023 Throwable cause = se.getRootCause();
1024 if (cause instanceof InstantiationException)
1025 throw (InstantiationException)cause;
1026 if (cause instanceof IllegalAccessException)
1027 throw (IllegalAccessException)cause;
1028 throw se;
1029 }
1030 }
1031
1032
1033
1034 @Override
1035 public String toString()
1036 {
1037 return String.format("%s@%x==%s,%d,%b",_name,hashCode(),_className,_initOrder,_servlet!=null);
1038 }
1039 }