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