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