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