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