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