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