blob: 43ce722899b3fa0246c50e8e880ebc65f49c2a82 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4// ------------------------------------------------------------------------
5// All rights reserved. This program and the accompanying materials
6// are made available under the terms of the Eclipse Public License v1.0
7// and Apache License v2.0 which accompanies this distribution.
8//
9// The Eclipse Public License is available at
10// http://www.eclipse.org/legal/epl-v10.html
11//
12// The Apache License v2.0 is available at
13// http://www.opensource.org/licenses/apache2.0.php
14//
15// You may elect to redistribute this code under either of these licenses.
16// ========================================================================
17//
18
19package org.eclipse.jetty.webapp;
20
21import java.io.File;
22import java.io.IOException;
23import java.net.URL;
24import java.net.URLClassLoader;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.EnumSet;
28import java.util.EventListener;
29import java.util.HashSet;
30import java.util.Iterator;
31import java.util.List;
32import java.util.Locale;
33import java.util.Map;
34import java.util.Set;
35
36import javax.servlet.DispatcherType;
37import javax.servlet.MultipartConfigElement;
38import javax.servlet.ServletException;
39import javax.servlet.ServletRegistration;
40import javax.servlet.SessionTrackingMode;
41import javax.servlet.descriptor.JspConfigDescriptor;
42import javax.servlet.descriptor.JspPropertyGroupDescriptor;
43import javax.servlet.descriptor.TaglibDescriptor;
44
45import org.eclipse.jetty.security.ConstraintAware;
46import org.eclipse.jetty.security.ConstraintMapping;
47import org.eclipse.jetty.security.authentication.FormAuthenticator;
48import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
49import org.eclipse.jetty.servlet.FilterHolder;
50import org.eclipse.jetty.servlet.FilterMapping;
51import org.eclipse.jetty.servlet.Holder;
52import org.eclipse.jetty.servlet.JspPropertyGroupServlet;
53import org.eclipse.jetty.servlet.ServletContextHandler;
54import org.eclipse.jetty.servlet.ServletHandler;
55import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
56import org.eclipse.jetty.servlet.ServletContextHandler.JspPropertyGroup;
57import org.eclipse.jetty.servlet.ServletContextHandler.TagLib;
58import org.eclipse.jetty.servlet.ServletHolder;
59import org.eclipse.jetty.servlet.ServletMapping;
60import org.eclipse.jetty.util.LazyList;
61import org.eclipse.jetty.util.Loader;
62import org.eclipse.jetty.util.log.Log;
63import org.eclipse.jetty.util.log.Logger;
64import org.eclipse.jetty.util.resource.Resource;
65import org.eclipse.jetty.util.security.Constraint;
66import org.eclipse.jetty.xml.XmlParser;
67
68/**
69 * StandardDescriptorProcessor
70 *
71 * Process a web.xml, web-defaults.xml, web-overrides.xml, web-fragment.xml.
72 */
73public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
74{
75 private static final Logger LOG = Log.getLogger(StandardDescriptorProcessor.class);
76
77 public static final String STANDARD_PROCESSOR = "org.eclipse.jetty.standardDescriptorProcessor";
78
79
80
81 public StandardDescriptorProcessor ()
82 {
83
84 try
85 {
86 registerVisitor("context-param", this.getClass().getDeclaredMethod("visitContextParam", __signature));
87 registerVisitor("display-name", this.getClass().getDeclaredMethod("visitDisplayName", __signature));
88 registerVisitor("servlet", this.getClass().getDeclaredMethod("visitServlet", __signature));
89 registerVisitor("servlet-mapping", this.getClass().getDeclaredMethod("visitServletMapping", __signature));
90 registerVisitor("session-config", this.getClass().getDeclaredMethod("visitSessionConfig", __signature));
91 registerVisitor("mime-mapping", this.getClass().getDeclaredMethod("visitMimeMapping", __signature));
92 registerVisitor("welcome-file-list", this.getClass().getDeclaredMethod("visitWelcomeFileList", __signature));
93 registerVisitor("locale-encoding-mapping-list", this.getClass().getDeclaredMethod("visitLocaleEncodingList", __signature));
94 registerVisitor("error-page", this.getClass().getDeclaredMethod("visitErrorPage", __signature));
95 registerVisitor("taglib", this.getClass().getDeclaredMethod("visitTagLib", __signature));
96 registerVisitor("jsp-config", this.getClass().getDeclaredMethod("visitJspConfig", __signature));
97 registerVisitor("security-constraint", this.getClass().getDeclaredMethod("visitSecurityConstraint", __signature));
98 registerVisitor("login-config", this.getClass().getDeclaredMethod("visitLoginConfig", __signature));
99 registerVisitor("security-role", this.getClass().getDeclaredMethod("visitSecurityRole", __signature));
100 registerVisitor("filter", this.getClass().getDeclaredMethod("visitFilter", __signature));
101 registerVisitor("filter-mapping", this.getClass().getDeclaredMethod("visitFilterMapping", __signature));
102 registerVisitor("listener", this.getClass().getDeclaredMethod("visitListener", __signature));
103 registerVisitor("distributable", this.getClass().getDeclaredMethod("visitDistributable", __signature));
104 }
105 catch (Exception e)
106 {
107 throw new IllegalStateException(e);
108 }
109 }
110
111
112
113 /**
114 * {@inheritDoc}
115 */
116 public void start(WebAppContext context, Descriptor descriptor)
117 {
118 }
119
120
121
122 /**
123 * {@inheritDoc}
124 */
125 public void end(WebAppContext context, Descriptor descriptor)
126 {
127 }
128
129 /**
130 * @param context
131 * @param descriptor
132 * @param node
133 */
134 public void visitContextParam (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
135 {
136 String name = node.getString("param-name", false, true);
137 String value = node.getString("param-value", false, true);
138 Origin o = context.getMetaData().getOrigin("context-param."+name);
139 switch (o)
140 {
141 case NotSet:
142 {
143 //just set it
144 context.getInitParams().put(name, value);
145 context.getMetaData().setOrigin("context-param."+name, descriptor);
146 break;
147 }
148 case WebXml:
149 case WebDefaults:
150 case WebOverride:
151 {
152 //previously set by a web xml, allow other web xml files to override
153 if (!(descriptor instanceof FragmentDescriptor))
154 {
155 context.getInitParams().put(name, value);
156 context.getMetaData().setOrigin("context-param."+name, descriptor);
157 }
158 break;
159 }
160 case WebFragment:
161 {
162 //previously set by a web-fragment, this fragment's value must be the same
163 if (descriptor instanceof FragmentDescriptor)
164 {
165 if (!((String)context.getInitParams().get(name)).equals(value))
166 throw new IllegalStateException("Conflicting context-param "+name+"="+value+" in "+descriptor.getResource());
167 }
168 break;
169 }
170 }
171 if (LOG.isDebugEnabled())
172 LOG.debug("ContextParam: " + name + "=" + value);
173
174 }
175
176
177 /* ------------------------------------------------------------ */
178 /**
179 * @param context
180 * @param descriptor
181 * @param node
182 */
183 protected void visitDisplayName(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
184 {
185 //Servlet Spec 3.0 p. 74 Ignore from web-fragments
186 if (!(descriptor instanceof FragmentDescriptor))
187 {
188 context.setDisplayName(node.toString(false, true));
189 context.getMetaData().setOrigin("display-name", descriptor);
190 }
191 }
192
193
194 /**
195 * @param context
196 * @param descriptor
197 * @param node
198 */
199 protected void visitServlet(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
200 {
201 String id = node.getAttribute("id");
202
203 // initialize holder
204 String servlet_name = node.getString("servlet-name", false, true);
205 ServletHolder holder = context.getServletHandler().getServlet(servlet_name);
206
207 /*
208 * If servlet of that name does not already exist, create it.
209 */
210 if (holder == null)
211 {
212 holder = context.getServletHandler().newServletHolder(Holder.Source.DESCRIPTOR);
213 holder.setName(servlet_name);
214 context.getServletHandler().addServlet(holder);
215 }
216
217 // init params
218 Iterator<?> iParamsIter = node.iterator("init-param");
219 while (iParamsIter.hasNext())
220 {
221 XmlParser.Node paramNode = (XmlParser.Node) iParamsIter.next();
222 String pname = paramNode.getString("param-name", false, true);
223 String pvalue = paramNode.getString("param-value", false, true);
224
225 Origin origin = context.getMetaData().getOrigin(servlet_name+".servlet.init-param."+pname);
226
227 switch (origin)
228 {
229 case NotSet:
230 {
231 //init-param not already set, so set it
232
233 holder.setInitParameter(pname, pvalue);
234 context.getMetaData().setOrigin(servlet_name+".servlet.init-param."+pname, descriptor);
235 break;
236 }
237 case WebXml:
238 case WebDefaults:
239 case WebOverride:
240 {
241 //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
242 //otherwise just ignore it
243 if (!(descriptor instanceof FragmentDescriptor))
244 {
245 holder.setInitParameter(pname, pvalue);
246 context.getMetaData().setOrigin(servlet_name+".servlet.init-param."+pname, descriptor);
247 }
248 break;
249 }
250 case WebFragment:
251 {
252 //previously set by a web-fragment, make sure that the value matches, otherwise its an error
253 if (!holder.getInitParameter(pname).equals(pvalue))
254 throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource());
255 break;
256 }
257 }
258 }
259
260 String servlet_class = node.getString("servlet-class", false, true);
261
262 // Handle JSP
263 String jspServletClass=null;;
264
265 //Handle the default jsp servlet instance
266 if (id != null && id.equals("jsp"))
267 {
268 jspServletClass = servlet_class;
269 try
270 {
271 Loader.loadClass(this.getClass(), servlet_class);
272
273 //Ensure there is a scratch dir
274 if (holder.getInitParameter("scratchdir") == null)
275 {
276 File tmp = context.getTempDirectory();
277 File scratch = new File(tmp, "jsp");
278 if (!scratch.exists()) scratch.mkdir();
279 holder.setInitParameter("scratchdir", scratch.getAbsolutePath());
280 }
281 }
282 catch (ClassNotFoundException e)
283 {
284 LOG.info("NO JSP Support for {}, did not find {}", context.getContextPath(), servlet_class);
285 jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet";
286 }
287 }
288
289
290 //Set the servlet-class
291 if (servlet_class != null)
292 {
293 ((WebDescriptor)descriptor).addClassName(servlet_class);
294
295 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.servlet-class");
296 switch (o)
297 {
298 case NotSet:
299 {
300 //the class of the servlet has not previously been set, so set it
301 holder.setClassName(servlet_class);
302 context.getMetaData().setOrigin(servlet_name+".servlet.servlet-class", descriptor);
303 break;
304 }
305 case WebXml:
306 case WebDefaults:
307 case WebOverride:
308 {
309 //the class of the servlet was set by a web xml file, only allow web-override/web-default to change it
310 if (!(descriptor instanceof FragmentDescriptor))
311 {
312 holder.setClassName(servlet_class);
313 context.getMetaData().setOrigin(servlet_name+".servlet.servlet-class", descriptor);
314 }
315 break;
316 }
317 case WebFragment:
318 {
319 //the class was set by another fragment, ensure this fragment's value is the same
320 if (!servlet_class.equals(holder.getClassName()))
321 throw new IllegalStateException("Conflicting servlet-class "+servlet_class+" in "+descriptor.getResource());
322 break;
323 }
324 }
325 }
326
327 // Handle JSP file
328 String jsp_file = node.getString("jsp-file", false, true);
329 if (jsp_file != null)
330 {
331 holder.setForcedPath(jsp_file);
332 ServletHolder jsp=context.getServletHandler().getServlet("jsp");
333 if (jsp!=null)
334 holder.setClassName(jsp.getClassName());
335 }
336
337 // handle load-on-startup
338 XmlParser.Node startup = node.get("load-on-startup");
339 if (startup != null)
340 {
341 String s = startup.toString(false, true).toLowerCase(Locale.ENGLISH);
342 int order = 0;
343 if (s.startsWith("t"))
344 {
345 LOG.warn("Deprecated boolean load-on-startup. Please use integer");
346 order = 1;
347 }
348 else
349 {
350 try
351 {
352 if (s != null && s.trim().length() > 0) order = Integer.parseInt(s);
353 }
354 catch (Exception e)
355 {
356 LOG.warn("Cannot parse load-on-startup " + s + ". Please use integer");
357 LOG.ignore(e);
358 }
359 }
360
361 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.load-on-startup");
362 switch (o)
363 {
364 case NotSet:
365 {
366 //not already set, so set it now
367 holder.setInitOrder(order);
368 context.getMetaData().setOrigin(servlet_name+".servlet.load-on-startup", descriptor);
369 break;
370 }
371 case WebXml:
372 case WebDefaults:
373 case WebOverride:
374 {
375 //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it
376 if (!(descriptor instanceof FragmentDescriptor))
377 {
378 holder.setInitOrder(order);
379 context.getMetaData().setOrigin(servlet_name+".servlet.load-on-startup", descriptor);
380 }
381 break;
382 }
383 case WebFragment:
384 {
385 //it was already set by another fragment, if we're parsing a fragment, the values must match
386 if (order != holder.getInitOrder())
387 throw new IllegalStateException("Conflicting load-on-startup value in "+descriptor.getResource());
388 break;
389 }
390 }
391 }
392
393 Iterator sRefsIter = node.iterator("security-role-ref");
394 while (sRefsIter.hasNext())
395 {
396 XmlParser.Node securityRef = (XmlParser.Node) sRefsIter.next();
397 String roleName = securityRef.getString("role-name", false, true);
398 String roleLink = securityRef.getString("role-link", false, true);
399 if (roleName != null && roleName.length() > 0 && roleLink != null && roleLink.length() > 0)
400 {
401 if (LOG.isDebugEnabled()) LOG.debug("link role " + roleName + " to " + roleLink + " for " + this);
402 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.role-name."+roleName);
403 switch (o)
404 {
405 case NotSet:
406 {
407 //set it
408 holder.setUserRoleLink(roleName, roleLink);
409 context.getMetaData().setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor);
410 break;
411 }
412 case WebXml:
413 case WebDefaults:
414 case WebOverride:
415 {
416 //only another web xml descriptor (web-default,web-override web.xml) can override an already set value
417 if (!(descriptor instanceof FragmentDescriptor))
418 {
419 holder.setUserRoleLink(roleName, roleLink);
420 context.getMetaData().setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor);
421 }
422 break;
423 }
424 case WebFragment:
425 {
426 if (!holder.getUserRoleLink(roleName).equals(roleLink))
427 throw new IllegalStateException("Conflicting role-link for role-name "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource());
428 break;
429 }
430 }
431 }
432 else
433 {
434 LOG.warn("Ignored invalid security-role-ref element: " + "servlet-name=" + holder.getName() + ", " + securityRef);
435 }
436 }
437
438
439 XmlParser.Node run_as = node.get("run-as");
440 if (run_as != null)
441 {
442 String roleName = run_as.getString("role-name", false, true);
443
444 if (roleName != null)
445 {
446 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.run-as");
447 switch (o)
448 {
449 case NotSet:
450 {
451 //run-as not set, so set it
452 holder.setRunAsRole(roleName);
453 context.getMetaData().setOrigin(servlet_name+".servlet.run-as", descriptor);
454 break;
455 }
456 case WebXml:
457 case WebDefaults:
458 case WebOverride:
459 {
460 //run-as was set by a web xml, only allow it to be changed if we're currently parsing another web xml(override/default)
461 if (!(descriptor instanceof FragmentDescriptor))
462 {
463 holder.setRunAsRole(roleName);
464 context.getMetaData().setOrigin(servlet_name+".servlet.run-as", descriptor);
465 }
466 break;
467 }
468 case WebFragment:
469 {
470 //run-as was set by another fragment, this fragment must show the same value
471 if (!holder.getRunAsRole().equals(roleName))
472 throw new IllegalStateException("Conflicting run-as role "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource());
473 break;
474 }
475 }
476 }
477 }
478
479 String async=node.getString("async-supported",false,true);
480 if (async!=null)
481 {
482 boolean val = async.length()==0||Boolean.valueOf(async);
483 Origin o =context.getMetaData().getOrigin(servlet_name+".servlet.async-supported");
484 switch (o)
485 {
486 case NotSet:
487 {
488 //set it
489 holder.setAsyncSupported(val);
490 context.getMetaData().setOrigin(servlet_name+".servlet.async-supported", descriptor);
491 break;
492 }
493 case WebXml:
494 case WebDefaults:
495 case WebOverride:
496 {
497 //async-supported set by previous web xml descriptor, only allow override if we're parsing another web descriptor(web.xml/web-override.xml/web-default.xml)
498 if (!(descriptor instanceof FragmentDescriptor))
499 {
500 holder.setAsyncSupported(val);
501 context.getMetaData().setOrigin(servlet_name+".servlet.async-supported", descriptor);
502 }
503 break;
504 }
505 case WebFragment:
506 {
507 //async-supported set by another fragment, this fragment's value must match
508 if (holder.isAsyncSupported() != val)
509 throw new IllegalStateException("Conflicting async-supported="+async+" for servlet "+servlet_name+" in "+descriptor.getResource());
510 break;
511 }
512 }
513 }
514
515 String enabled = node.getString("enabled", false, true);
516 if (enabled!=null)
517 {
518 boolean is_enabled = enabled.length()==0||Boolean.valueOf(enabled);
519 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.enabled");
520 switch (o)
521 {
522 case NotSet:
523 {
524 //hasn't been set yet, so set it
525 holder.setEnabled(is_enabled);
526 context.getMetaData().setOrigin(servlet_name+".servlet.enabled", descriptor);
527 break;
528 }
529 case WebXml:
530 case WebDefaults:
531 case WebOverride:
532 {
533 //was set in a web xml descriptor, only allow override from another web xml descriptor
534 if (!(descriptor instanceof FragmentDescriptor))
535 {
536 holder.setEnabled(is_enabled);
537 context.getMetaData().setOrigin(servlet_name+".servlet.enabled", descriptor);
538 }
539 break;
540 }
541 case WebFragment:
542 {
543 //was set by another fragment, this fragment's value must match
544 if (holder.isEnabled() != is_enabled)
545 throw new IllegalStateException("Conflicting value of servlet enabled for servlet "+servlet_name+" in "+descriptor.getResource());
546 break;
547 }
548 }
549 }
550
551 /*
552 * If multipart config not set, then set it and record it was by the web.xml or fragment.
553 * If it was set by web.xml then if this is a fragment, ignore the settings.
554 * If it was set by a fragment, if this is a fragment and the values are different, error!
555 */
556 XmlParser.Node multipart = node.get("multipart-config");
557 if (multipart != null)
558 {
559 String location = multipart.getString("location", false, true);
560 String maxFile = multipart.getString("max-file-size", false, true);
561 String maxRequest = multipart.getString("max-request-size", false, true);
562 String threshold = multipart.getString("file-size-threshold",false,true);
563 MultipartConfigElement element = new MultipartConfigElement(location,
564 (maxFile==null||"".equals(maxFile)?-1L:Long.parseLong(maxFile)),
565 (maxRequest==null||"".equals(maxRequest)?-1L:Long.parseLong(maxRequest)),
566 (threshold==null||"".equals(threshold)?0:Integer.parseInt(threshold)));
567
568 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.multipart-config");
569 switch (o)
570 {
571 case NotSet:
572 {
573 //hasn't been set, so set it
574 holder.getRegistration().setMultipartConfig(element);
575 context.getMetaData().setOrigin(servlet_name+".servlet.multipart-config", descriptor);
576 break;
577 }
578 case WebXml:
579 case WebDefaults:
580 case WebOverride:
581 {
582 //was set in a web xml, only allow changes if we're parsing another web xml (web.xml/web-default.xml/web-override.xml)
583 if (!(descriptor instanceof FragmentDescriptor))
584 {
585 holder.getRegistration().setMultipartConfig(element);
586 context.getMetaData().setOrigin(servlet_name+".servlet.multipart-config", descriptor);
587 }
588 break;
589 }
590 case WebFragment:
591 {
592 //another fragment set the value, this fragment's values must match exactly or it is an error
593 MultipartConfigElement cfg = ((ServletHolder.Registration)holder.getRegistration()).getMultipartConfig();
594
595 if (cfg.getMaxFileSize() != element.getMaxFileSize())
596 throw new IllegalStateException("Conflicting multipart-config max-file-size for servlet "+servlet_name+" in "+descriptor.getResource());
597 if (cfg.getMaxRequestSize() != element.getMaxRequestSize())
598 throw new IllegalStateException("Conflicting multipart-config max-request-size for servlet "+servlet_name+" in "+descriptor.getResource());
599 if (cfg.getFileSizeThreshold() != element.getFileSizeThreshold())
600 throw new IllegalStateException("Conflicting multipart-config file-size-threshold for servlet "+servlet_name+" in "+descriptor.getResource());
601 if ((cfg.getLocation() != null && (element.getLocation() == null || element.getLocation().length()==0))
602 || (cfg.getLocation() == null && (element.getLocation()!=null || element.getLocation().length() > 0)))
603 throw new IllegalStateException("Conflicting multipart-config location for servlet "+servlet_name+" in "+descriptor.getResource());
604 break;
605 }
606 }
607 }
608 }
609
610
611
612 /**
613 * @param context
614 * @param descriptor
615 * @param node
616 */
617 protected void visitServletMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
618 {
619 //Servlet Spec 3.0, p74
620 //servlet-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments.
621 //Maintenance update 3.0a to spec:
622 // Updated 8.2.3.g.v to say <servlet-mapping> elements are additive across web-fragments.
623 // <servlet-mapping> declared in web.xml overrides the mapping for the servlet specified in the web-fragment.xml
624
625 String servlet_name = node.getString("servlet-name", false, true);
626 Origin origin = context.getMetaData().getOrigin(servlet_name+".servlet.mappings");
627
628 switch (origin)
629 {
630 case NotSet:
631 {
632 //no servlet mappings
633 context.getMetaData().setOrigin(servlet_name+".servlet.mappings", descriptor);
634 ServletMapping mapping = addServletMapping(servlet_name, node, context, descriptor);
635 mapping.setDefault(context.getMetaData().getOrigin(servlet_name+".servlet.mappings") == Origin.WebDefaults);
636 break;
637 }
638 case WebXml:
639 case WebDefaults:
640 case WebOverride:
641 {
642 //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
643 //otherwise just ignore it
644 if (!(descriptor instanceof FragmentDescriptor))
645 {
646 addServletMapping(servlet_name, node, context, descriptor);
647 }
648 break;
649 }
650 case WebFragment:
651 {
652 //mappings previously set by another web-fragment, so merge in this web-fragment's mappings
653 addServletMapping(servlet_name, node, context, descriptor);
654 break;
655 }
656 }
657 }
658
659
660 /**
661 * @param context
662 * @param descriptor
663 * @param node
664 */
665 protected void visitSessionConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
666 {
667 XmlParser.Node tNode = node.get("session-timeout");
668 if (tNode != null)
669 {
670 int timeout = Integer.parseInt(tNode.toString(false, true));
671 context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60);
672 }
673
674 //Servlet Spec 3.0
675 // <tracking-mode>
676 // this is additive across web-fragments
677 Iterator iter = node.iterator("tracking-mode");
678 if (iter.hasNext())
679 {
680 Set<SessionTrackingMode> modes = null;
681 Origin o = context.getMetaData().getOrigin("session.tracking-mode");
682 switch (o)
683 {
684 case NotSet://not previously set, starting fresh
685 case WebDefaults://previously set in web defaults, allow this descriptor to start fresh
686 {
687
688 modes = new HashSet<SessionTrackingMode>();
689 context.getMetaData().setOrigin("session.tracking-mode", descriptor);
690 break;
691 }
692 case WebXml:
693 case WebFragment:
694 case WebOverride:
695 {
696 //if setting from an override descriptor, start afresh, otherwise add-in tracking-modes
697 if (descriptor instanceof OverrideDescriptor)
698 modes = new HashSet<SessionTrackingMode>();
699 else
700 modes = new HashSet<SessionTrackingMode>(context.getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes());
701 context.getMetaData().setOrigin("session.tracking-mode", descriptor);
702 break;
703 }
704 }
705
706 while (iter.hasNext())
707 {
708 XmlParser.Node mNode = (XmlParser.Node) iter.next();
709 String trackMode = mNode.toString(false, true);
710 modes.add(SessionTrackingMode.valueOf(trackMode));
711 }
712 context.getSessionHandler().getSessionManager().setSessionTrackingModes(modes);
713 }
714
715
716 //Servlet Spec 3.0
717 //<cookie-config>
718 XmlParser.Node cookieConfig = node.get("cookie-config");
719 if (cookieConfig != null)
720 {
721 // <name>
722 String name = cookieConfig.getString("name", false, true);
723 if (name != null)
724 {
725 Origin o = context.getMetaData().getOrigin("cookie-config.name");
726 switch (o)
727 {
728 case NotSet:
729 {
730 //no <cookie-config><name> set yet, accept it
731 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setName(name);
732 context.getMetaData().setOrigin("cookie-config.name", descriptor);
733 break;
734 }
735 case WebXml:
736 case WebDefaults:
737 case WebOverride:
738 {
739 //<cookie-config><name> set in a web xml, only allow web-default/web-override to change
740 if (!(descriptor instanceof FragmentDescriptor))
741 {
742 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setName(name);
743 context.getMetaData().setOrigin("cookie-config.name", descriptor);
744 }
745 break;
746 }
747 case WebFragment:
748 {
749 //a web-fragment set the value, all web-fragments must have the same value
750 if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getName().equals(name))
751 throw new IllegalStateException("Conflicting cookie-config name "+name+" in "+descriptor.getResource());
752 break;
753 }
754 }
755 }
756
757 // <domain>
758 String domain = cookieConfig.getString("domain", false, true);
759 if (domain != null)
760 {
761 Origin o = context.getMetaData().getOrigin("cookie-config.domain");
762 switch (o)
763 {
764 case NotSet:
765 {
766 //no <cookie-config><domain> set yet, accept it
767 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setDomain(domain);
768 context.getMetaData().setOrigin("cookie-config.domain", descriptor);
769 break;
770 }
771 case WebXml:
772 case WebDefaults:
773 case WebOverride:
774 {
775 //<cookie-config><domain> set in a web xml, only allow web-default/web-override to change
776 if (!(descriptor instanceof FragmentDescriptor))
777 {
778 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setDomain(domain);
779 context.getMetaData().setOrigin("cookie-config.domain", descriptor);
780 }
781 break;
782 }
783 case WebFragment:
784 {
785 //a web-fragment set the value, all web-fragments must have the same value
786 if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getDomain().equals(domain))
787 throw new IllegalStateException("Conflicting cookie-config domain "+domain+" in "+descriptor.getResource());
788 break;
789 }
790 }
791 }
792
793 // <path>
794 String path = cookieConfig.getString("path", false, true);
795 if (path != null)
796 {
797 Origin o = context.getMetaData().getOrigin("cookie-config.path");
798 switch (o)
799 {
800 case NotSet:
801 {
802 //no <cookie-config><domain> set yet, accept it
803 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setPath(path);
804 context.getMetaData().setOrigin("cookie-config.path", descriptor);
805 break;
806 }
807 case WebXml:
808 case WebDefaults:
809 case WebOverride:
810 {
811 //<cookie-config><domain> set in a web xml, only allow web-default/web-override to change
812 if (!(descriptor instanceof FragmentDescriptor))
813 {
814 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setPath(path);
815 context.getMetaData().setOrigin("cookie-config.path", descriptor);
816 }
817 break;
818 }
819 case WebFragment:
820 {
821 //a web-fragment set the value, all web-fragments must have the same value
822 if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getPath().equals(path))
823 throw new IllegalStateException("Conflicting cookie-config path "+path+" in "+descriptor.getResource());
824 break;
825 }
826 }
827 }
828
829 // <comment>
830 String comment = cookieConfig.getString("comment", false, true);
831 if (comment != null)
832 {
833 Origin o = context.getMetaData().getOrigin("cookie-config.comment");
834 switch (o)
835 {
836 case NotSet:
837 {
838 //no <cookie-config><comment> set yet, accept it
839 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setComment(comment);
840 context.getMetaData().setOrigin("cookie-config.comment", descriptor);
841 break;
842 }
843 case WebXml:
844 case WebDefaults:
845 case WebOverride:
846 {
847 //<cookie-config><comment> set in a web xml, only allow web-default/web-override to change
848 if (!(descriptor instanceof FragmentDescriptor))
849 {
850 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setComment(comment);
851 context.getMetaData().setOrigin("cookie-config.comment", descriptor);
852 }
853 break;
854 }
855 case WebFragment:
856 {
857 //a web-fragment set the value, all web-fragments must have the same value
858 if (!context.getSessionHandler().getSessionManager().getSessionCookieConfig().getComment().equals(comment))
859 throw new IllegalStateException("Conflicting cookie-config comment "+comment+" in "+descriptor.getResource());
860 break;
861 }
862 }
863 }
864
865 // <http-only>true/false
866 tNode = cookieConfig.get("http-only");
867 if (tNode != null)
868 {
869 boolean httpOnly = Boolean.parseBoolean(tNode.toString(false,true));
870 Origin o = context.getMetaData().getOrigin("cookie-config.http-only");
871 switch (o)
872 {
873 case NotSet:
874 {
875 //no <cookie-config><http-only> set yet, accept it
876 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setHttpOnly(httpOnly);
877 context.getMetaData().setOrigin("cookie-config.http-only", descriptor);
878 break;
879 }
880 case WebXml:
881 case WebDefaults:
882 case WebOverride:
883 {
884 //<cookie-config><http-only> set in a web xml, only allow web-default/web-override to change
885 if (!(descriptor instanceof FragmentDescriptor))
886 {
887 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setHttpOnly(httpOnly);
888 context.getMetaData().setOrigin("cookie-config.http-only", descriptor);
889 }
890 break;
891 }
892 case WebFragment:
893 {
894 //a web-fragment set the value, all web-fragments must have the same value
895 if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().isHttpOnly() != httpOnly)
896 throw new IllegalStateException("Conflicting cookie-config http-only "+httpOnly+" in "+descriptor.getResource());
897 break;
898 }
899 }
900 }
901
902 // <secure>true/false
903 tNode = cookieConfig.get("secure");
904 if (tNode != null)
905 {
906 boolean secure = Boolean.parseBoolean(tNode.toString(false,true));
907 Origin o = context.getMetaData().getOrigin("cookie-config.secure");
908 switch (o)
909 {
910 case NotSet:
911 {
912 //no <cookie-config><secure> set yet, accept it
913 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setSecure(secure);
914 context.getMetaData().setOrigin("cookie-config.secure", descriptor);
915 break;
916 }
917 case WebXml:
918 case WebDefaults:
919 case WebOverride:
920 {
921 //<cookie-config><secure> set in a web xml, only allow web-default/web-override to change
922 if (!(descriptor instanceof FragmentDescriptor))
923 {
924 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setSecure(secure);
925 context.getMetaData().setOrigin("cookie-config.secure", descriptor);
926 }
927 break;
928 }
929 case WebFragment:
930 {
931 //a web-fragment set the value, all web-fragments must have the same value
932 if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().isSecure() != secure)
933 throw new IllegalStateException("Conflicting cookie-config secure "+secure+" in "+descriptor.getResource());
934 break;
935 }
936 }
937 }
938
939 // <max-age>
940 tNode = cookieConfig.get("max-age");
941 if (tNode != null)
942 {
943 int maxAge = Integer.parseInt(tNode.toString(false,true));
944 Origin o = context.getMetaData().getOrigin("cookie-config.max-age");
945 switch (o)
946 {
947 case NotSet:
948 {
949 //no <cookie-config><max-age> set yet, accept it
950 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setMaxAge(maxAge);
951 context.getMetaData().setOrigin("cookie-config.max-age", descriptor);
952 break;
953 }
954 case WebXml:
955 case WebDefaults:
956 case WebOverride:
957 {
958 //<cookie-config><max-age> set in a web xml, only allow web-default/web-override to change
959 if (!(descriptor instanceof FragmentDescriptor))
960 {
961 context.getSessionHandler().getSessionManager().getSessionCookieConfig().setMaxAge(maxAge);
962 context.getMetaData().setOrigin("cookie-config.max-age", descriptor);
963 }
964 break;
965 }
966 case WebFragment:
967 {
968 //a web-fragment set the value, all web-fragments must have the same value
969 if (context.getSessionHandler().getSessionManager().getSessionCookieConfig().getMaxAge() != maxAge)
970 throw new IllegalStateException("Conflicting cookie-config max-age "+maxAge+" in "+descriptor.getResource());
971 break;
972 }
973 }
974 }
975 }
976 }
977
978
979
980 /**
981 * @param context
982 * @param descriptor
983 * @param node
984 */
985 protected void visitMimeMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
986 {
987 String extension = node.getString("extension", false, true);
988 if (extension != null && extension.startsWith("."))
989 extension = extension.substring(1);
990 String mimeType = node.getString("mime-type", false, true);
991 if (extension != null)
992 {
993 Origin o = context.getMetaData().getOrigin("extension."+extension);
994 switch (o)
995 {
996 case NotSet:
997 {
998 //no mime-type set for the extension yet
999 context.getMimeTypes().addMimeMapping(extension, mimeType);
1000 context.getMetaData().setOrigin("extension."+extension, descriptor);
1001 break;
1002 }
1003 case WebXml:
1004 case WebDefaults:
1005 case WebOverride:
1006 {
1007 //a mime-type was set for the extension in a web xml, only allow web-default/web-override to change
1008 if (!(descriptor instanceof FragmentDescriptor))
1009 {
1010 context.getMimeTypes().addMimeMapping(extension, mimeType);
1011 context.getMetaData().setOrigin("extension."+extension, descriptor);
1012 }
1013 break;
1014 }
1015 case WebFragment:
1016 {
1017 //a web-fragment set the value, all web-fragments must have the same value
1018 if (!context.getMimeTypes().getMimeByExtension("."+extension).equals(context.getMimeTypes().CACHE.lookup(mimeType)))
1019 throw new IllegalStateException("Conflicting mime-type "+mimeType+" for extension "+extension+" in "+descriptor.getResource());
1020 break;
1021 }
1022 }
1023 }
1024 }
1025
1026 /**
1027 * @param context
1028 * @param descriptor
1029 * @param node
1030 */
1031 protected void visitWelcomeFileList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1032 {
1033 Origin o = context.getMetaData().getOrigin("welcome-file-list");
1034 switch (o)
1035 {
1036 case NotSet:
1037 {
1038 context.getMetaData().setOrigin("welcome-file-list", descriptor);
1039 addWelcomeFiles(context,node);
1040 break;
1041 }
1042 case WebXml:
1043 {
1044 //web.xml set the welcome-file-list, all other descriptors then just merge in
1045 addWelcomeFiles(context,node);
1046 break;
1047 }
1048 case WebDefaults:
1049 {
1050 //if web-defaults set the welcome-file-list first and
1051 //we're processing web.xml then reset the welcome-file-list
1052 if (!(descriptor instanceof DefaultsDescriptor) && !(descriptor instanceof OverrideDescriptor) && !(descriptor instanceof FragmentDescriptor))
1053 {
1054 context.setWelcomeFiles(new String[0]);
1055 }
1056 addWelcomeFiles(context,node);
1057 break;
1058 }
1059 case WebOverride:
1060 {
1061 //web-override set the list, all other descriptors just merge in
1062 addWelcomeFiles(context,node);
1063 break;
1064 }
1065 case WebFragment:
1066 {
1067 //A web-fragment first set the welcome-file-list. Other descriptors just add.
1068 addWelcomeFiles(context,node);
1069 break;
1070 }
1071 }
1072 }
1073
1074 /**
1075 * @param context
1076 * @param descriptor
1077 * @param node
1078 */
1079 protected void visitLocaleEncodingList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1080 {
1081 Iterator<XmlParser.Node> iter = node.iterator("locale-encoding-mapping");
1082 while (iter.hasNext())
1083 {
1084 XmlParser.Node mapping = iter.next();
1085 String locale = mapping.getString("locale", false, true);
1086 String encoding = mapping.getString("encoding", false, true);
1087
1088 if (encoding != null)
1089 {
1090 Origin o = context.getMetaData().getOrigin("locale-encoding."+locale);
1091 switch (o)
1092 {
1093 case NotSet:
1094 {
1095 //no mapping for the locale yet, so set it
1096 context.addLocaleEncoding(locale, encoding);
1097 context.getMetaData().setOrigin("locale-encoding."+locale, descriptor);
1098 break;
1099 }
1100 case WebXml:
1101 case WebDefaults:
1102 case WebOverride:
1103 {
1104 //a value was set in a web descriptor, only allow another web descriptor to change it (web-default/web-override)
1105 if (!(descriptor instanceof FragmentDescriptor))
1106 {
1107 context.addLocaleEncoding(locale, encoding);
1108 context.getMetaData().setOrigin("locale-encoding."+locale, descriptor);
1109 }
1110 break;
1111 }
1112 case WebFragment:
1113 {
1114 //a value was set by a web-fragment, all fragments must have the same value
1115 if (!encoding.equals(context.getLocaleEncoding(locale)))
1116 throw new IllegalStateException("Conflicting loacle-encoding mapping for locale "+locale+" in "+descriptor.getResource());
1117 break;
1118 }
1119 }
1120 }
1121 }
1122 }
1123
1124 /**
1125 * @param context
1126 * @param descriptor
1127 * @param node
1128 */
1129 protected void visitErrorPage(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1130 {
1131 String error = node.getString("error-code", false, true);
1132 int code=0;
1133 if (error == null || error.length() == 0)
1134 {
1135 error = node.getString("exception-type", false, true);
1136 if (error == null || error.length() == 0)
1137 error = ErrorPageErrorHandler.GLOBAL_ERROR_PAGE;
1138 }
1139 else
1140 code=Integer.valueOf(error);
1141
1142 String location = node.getString("location", false, true);
1143 ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler();
1144 Origin o = context.getMetaData().getOrigin("error."+error);
1145
1146 switch (o)
1147 {
1148 case NotSet:
1149 {
1150 //no error page setup for this code or exception yet
1151 if (code>0)
1152 handler.addErrorPage(code,location);
1153 else
1154 handler.addErrorPage(error,location);
1155 context.getMetaData().setOrigin("error."+error, descriptor);
1156 break;
1157 }
1158 case WebXml:
1159 case WebDefaults:
1160 case WebOverride:
1161 {
1162 //an error page setup was set in web.xml, only allow other web xml descriptors to override it
1163 if (!(descriptor instanceof FragmentDescriptor))
1164 {
1165 if (descriptor instanceof OverrideDescriptor || descriptor instanceof DefaultsDescriptor)
1166 {
1167 if (code>0)
1168 handler.addErrorPage(code,location);
1169 else
1170 handler.addErrorPage(error,location);
1171 context.getMetaData().setOrigin("error."+error, descriptor);
1172 }
1173 else
1174 throw new IllegalStateException("Duplicate global error-page "+location);
1175 }
1176 break;
1177 }
1178 case WebFragment:
1179 {
1180 //another web fragment set the same error code or exception, if its different its an error
1181 if (!handler.getErrorPages().get(error).equals(location))
1182 throw new IllegalStateException("Conflicting error-code or exception-type "+error+" in "+descriptor.getResource());
1183 break;
1184 }
1185 }
1186
1187 }
1188
1189 /**
1190 * @param context
1191 * @param node
1192 */
1193 protected void addWelcomeFiles(WebAppContext context, XmlParser.Node node)
1194 {
1195 Iterator<XmlParser.Node> iter = node.iterator("welcome-file");
1196 while (iter.hasNext())
1197 {
1198 XmlParser.Node indexNode = (XmlParser.Node) iter.next();
1199 String welcome = indexNode.toString(false, true);
1200
1201 //Servlet Spec 3.0 p. 74 welcome files are additive
1202 if (welcome != null && welcome.trim().length() > 0)
1203 context.setWelcomeFiles((String[])LazyList.addToArray(context.getWelcomeFiles(),welcome,String.class));
1204 }
1205 }
1206
1207
1208 /**
1209 * @param servletName
1210 * @param node
1211 * @param context
1212 */
1213 protected ServletMapping addServletMapping (String servletName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
1214 {
1215 ServletMapping mapping = new ServletMapping();
1216 mapping.setServletName(servletName);
1217
1218 List<String> paths = new ArrayList<String>();
1219 Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
1220 while (iter.hasNext())
1221 {
1222 String p = iter.next().toString(false, true);
1223 p = normalizePattern(p);
1224 paths.add(p);
1225 context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, descriptor);
1226 }
1227 mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
1228 context.getServletHandler().addServletMapping(mapping);
1229 return mapping;
1230 }
1231
1232 /**
1233 * @param filterName
1234 * @param node
1235 * @param context
1236 */
1237 protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context, Descriptor descriptor)
1238 {
1239 FilterMapping mapping = new FilterMapping();
1240 mapping.setFilterName(filterName);
1241
1242 List<String> paths = new ArrayList<String>();
1243 Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
1244 while (iter.hasNext())
1245 {
1246 String p = iter.next().toString(false, true);
1247 p = normalizePattern(p);
1248 paths.add(p);
1249 context.getMetaData().setOrigin(filterName+".filter.mapping."+p, descriptor);
1250 }
1251 mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
1252
1253 List<String> names = new ArrayList<String>();
1254 iter = node.iterator("servlet-name");
1255 while (iter.hasNext())
1256 {
1257 String n = ((XmlParser.Node) iter.next()).toString(false, true);
1258 names.add(n);
1259 }
1260 mapping.setServletNames((String[]) names.toArray(new String[names.size()]));
1261
1262
1263 List<DispatcherType> dispatches = new ArrayList<DispatcherType>();
1264 iter=node.iterator("dispatcher");
1265 while(iter.hasNext())
1266 {
1267 String d=((XmlParser.Node)iter.next()).toString(false,true);
1268 dispatches.add(FilterMapping.dispatch(d));
1269 }
1270
1271 if (dispatches.size()>0)
1272 mapping.setDispatcherTypes(EnumSet.copyOf(dispatches));
1273
1274 context.getServletHandler().addFilterMapping(mapping);
1275 }
1276
1277
1278 /**
1279 * @param context
1280 * @param descriptor
1281 * @param node
1282 */
1283 protected void visitTagLib(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1284 {
1285 //Additive across web.xml and web-fragment.xml
1286 String uri = node.getString("taglib-uri", false, true);
1287 String location = node.getString("taglib-location", false, true);
1288
1289 context.setResourceAlias(uri, location);
1290
1291 JspConfig config = (JspConfig)context.getServletContext().getJspConfigDescriptor();
1292 if (config == null)
1293 {
1294 config = new JspConfig();
1295 context.getServletContext().setJspConfigDescriptor(config);
1296 }
1297
1298 TagLib tl = new TagLib();
1299 tl.setTaglibLocation(location);
1300 tl.setTaglibURI(uri);
1301 config.addTaglibDescriptor(tl);
1302 }
1303
1304 /**
1305 * @param context
1306 * @param descriptor
1307 * @param node
1308 */
1309 protected void visitJspConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1310 {
1311 //Additive across web.xml and web-fragment.xml
1312 JspConfig config = (JspConfig)context.getServletContext().getJspConfigDescriptor();
1313 if (config == null)
1314 {
1315 config = new JspConfig();
1316 context.getServletContext().setJspConfigDescriptor(config);
1317 }
1318
1319
1320 for (int i = 0; i < node.size(); i++)
1321 {
1322 Object o = node.get(i);
1323 if (o instanceof XmlParser.Node && "taglib".equals(((XmlParser.Node) o).getTag()))
1324 visitTagLib(context,descriptor, (XmlParser.Node) o);
1325 }
1326
1327 // Map URLs from jsp property groups to JSP servlet.
1328 // this is more JSP stupidness creeping into the servlet spec
1329 Iterator<XmlParser.Node> iter = node.iterator("jsp-property-group");
1330 List<String> paths = new ArrayList<String>();
1331 while (iter.hasNext())
1332 {
1333 JspPropertyGroup jpg = new JspPropertyGroup();
1334 config.addJspPropertyGroup(jpg);
1335 XmlParser.Node group = iter.next();
1336
1337 //url-patterns
1338 Iterator<XmlParser.Node> iter2 = group.iterator("url-pattern");
1339 while (iter2.hasNext())
1340 {
1341 String url = iter2.next().toString(false, true);
1342 url = normalizePattern(url);
1343 paths.add( url);
1344 jpg.addUrlPattern(url);
1345 }
1346
1347 jpg.setElIgnored(group.getString("el-ignored", false, true));
1348 jpg.setPageEncoding(group.getString("page-encoding", false, true));
1349 jpg.setScriptingInvalid(group.getString("scripting-invalid", false, true));
1350 jpg.setIsXml(group.getString("is-xml", false, true));
1351 jpg.setDeferredSyntaxAllowedAsLiteral(group.getString("deferred-syntax-allowed-as-literal", false, true));
1352 jpg.setTrimDirectiveWhitespaces(group.getString("trim-directive-whitespaces", false, true));
1353 jpg.setDefaultContentType(group.getString("default-content-type", false, true));
1354 jpg.setBuffer(group.getString("buffer", false, true));
1355 jpg.setErrorOnUndeclaredNamespace(group.getString("error-on-undeclared-namespace", false, true));
1356
1357 //preludes
1358 Iterator<XmlParser.Node> preludes = group.iterator("include-prelude");
1359 while (preludes.hasNext())
1360 {
1361 String prelude = preludes.next().toString(false, true);
1362 jpg.addIncludePrelude(prelude);
1363 }
1364 //codas
1365 Iterator<XmlParser.Node> codas = group.iterator("include-coda");
1366 while (codas.hasNext())
1367 {
1368 String coda = codas.next().toString(false, true);
1369 jpg.addIncludeCoda(coda);
1370 }
1371
1372 if (LOG.isDebugEnabled()) LOG.debug(config.toString());
1373 }
1374
1375 if (paths.size() > 0)
1376 {
1377 ServletHandler handler = context.getServletHandler();
1378 ServletHolder jsp_pg_servlet = handler.getServlet(JspPropertyGroupServlet.NAME);
1379 if (jsp_pg_servlet==null)
1380 {
1381 jsp_pg_servlet=new ServletHolder(JspPropertyGroupServlet.NAME,new JspPropertyGroupServlet(context,handler));
1382 handler.addServlet(jsp_pg_servlet);
1383 }
1384
1385 ServletMapping mapping = new ServletMapping();
1386 mapping.setServletName(JspPropertyGroupServlet.NAME);
1387 mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
1388 context.getServletHandler().addServletMapping(mapping);
1389 }
1390 }
1391
1392 /**
1393 * @param context
1394 * @param descriptor
1395 * @param node
1396 */
1397 protected void visitSecurityConstraint(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1398 {
1399 Constraint scBase = new Constraint();
1400
1401 //ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive
1402 //across fragments
1403
1404 //TODO: need to remember origin of the constraints
1405 try
1406 {
1407 XmlParser.Node auths = node.get("auth-constraint");
1408
1409 if (auths != null)
1410 {
1411 scBase.setAuthenticate(true);
1412 // auth-constraint
1413 Iterator<XmlParser.Node> iter = auths.iterator("role-name");
1414 List<String> roles = new ArrayList<String>();
1415 while (iter.hasNext())
1416 {
1417 String role = iter.next().toString(false, true);
1418 roles.add(role);
1419 }
1420 scBase.setRoles(roles.toArray(new String[roles.size()]));
1421 }
1422
1423 XmlParser.Node data = node.get("user-data-constraint");
1424 if (data != null)
1425 {
1426 data = data.get("transport-guarantee");
1427 String guarantee = data.toString(false, true).toUpperCase(Locale.ENGLISH);
1428 if (guarantee == null || guarantee.length() == 0 || "NONE".equals(guarantee))
1429 scBase.setDataConstraint(Constraint.DC_NONE);
1430 else if ("INTEGRAL".equals(guarantee))
1431 scBase.setDataConstraint(Constraint.DC_INTEGRAL);
1432 else if ("CONFIDENTIAL".equals(guarantee))
1433 scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
1434 else
1435 {
1436 LOG.warn("Unknown user-data-constraint:" + guarantee);
1437 scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
1438 }
1439 }
1440 Iterator<XmlParser.Node> iter = node.iterator("web-resource-collection");
1441 while (iter.hasNext())
1442 {
1443 XmlParser.Node collection = iter.next();
1444 String name = collection.getString("web-resource-name", false, true);
1445 Constraint sc = (Constraint) scBase.clone();
1446 sc.setName(name);
1447
1448 Iterator<XmlParser.Node> iter2 = collection.iterator("url-pattern");
1449 while (iter2.hasNext())
1450 {
1451 String url = iter2.next().toString(false, true);
1452 url = normalizePattern(url);
1453 //remember origin so we can process ServletRegistration.Dynamic.setServletSecurityElement() correctly
1454 context.getMetaData().setOrigin("constraint.url."+url, descriptor);
1455
1456 Iterator<XmlParser.Node> iter3 = collection.iterator("http-method");
1457 Iterator<XmlParser.Node> iter4 = collection.iterator("http-method-omission");
1458
1459 if (iter3.hasNext())
1460 {
1461 if (iter4.hasNext())
1462 throw new IllegalStateException ("web-resource-collection cannot contain both http-method and http-method-omission");
1463
1464 //configure all the http-method elements for each url
1465 while (iter3.hasNext())
1466 {
1467 String method = ((XmlParser.Node) iter3.next()).toString(false, true);
1468 ConstraintMapping mapping = new ConstraintMapping();
1469 mapping.setMethod(method);
1470 mapping.setPathSpec(url);
1471 mapping.setConstraint(sc);
1472 ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
1473 }
1474 }
1475 else if (iter4.hasNext())
1476 {
1477 //configure all the http-method-omission elements for each url
1478 while (iter4.hasNext())
1479 {
1480 String method = ((XmlParser.Node)iter4.next()).toString(false, true);
1481 ConstraintMapping mapping = new ConstraintMapping();
1482 mapping.setMethodOmissions(new String[]{method});
1483 mapping.setPathSpec(url);
1484 mapping.setConstraint(sc);
1485 ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
1486 }
1487 }
1488 else
1489 {
1490 //No http-methods or http-method-omissions specified, the constraint applies to all
1491 ConstraintMapping mapping = new ConstraintMapping();
1492 mapping.setPathSpec(url);
1493 mapping.setConstraint(sc);
1494 ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
1495 }
1496 }
1497 }
1498 }
1499 catch (CloneNotSupportedException e)
1500 {
1501 LOG.warn(e);
1502 }
1503 }
1504
1505 /**
1506 * @param context
1507 * @param descriptor
1508 * @param node
1509 * @throws Exception
1510 */
1511 protected void visitLoginConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node) throws Exception
1512 {
1513 //ServletSpec 3.0 p74 says elements present 0/1 time if specified in web.xml take
1514 //precendece over any web-fragment. If not specified in web.xml, then if specified
1515 //in a web-fragment must be the same across all web-fragments.
1516 XmlParser.Node method = node.get("auth-method");
1517 if (method != null)
1518 {
1519 //handle auth-method merge
1520 Origin o = context.getMetaData().getOrigin("auth-method");
1521 switch (o)
1522 {
1523 case NotSet:
1524 {
1525 //not already set, so set it now
1526 context.getSecurityHandler().setAuthMethod(method.toString(false, true));
1527 context.getMetaData().setOrigin("auth-method", descriptor);
1528 break;
1529 }
1530 case WebXml:
1531 case WebDefaults:
1532 case WebOverride:
1533 {
1534 //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it
1535 if (!(descriptor instanceof FragmentDescriptor))
1536 {
1537 context.getSecurityHandler().setAuthMethod(method.toString(false, true));
1538 context.getMetaData().setOrigin("auth-method", descriptor);
1539 }
1540 break;
1541 }
1542 case WebFragment:
1543 {
1544 //it was already set by another fragment, if we're parsing a fragment, the values must match
1545 if (!context.getSecurityHandler().getAuthMethod().equals(method.toString(false, true)))
1546 throw new IllegalStateException("Conflicting auth-method value in "+descriptor.getResource());
1547 break;
1548 }
1549 }
1550
1551 //handle realm-name merge
1552 XmlParser.Node name = node.get("realm-name");
1553 String nameStr = (name == null ? "default" : name.toString(false, true));
1554 o = context.getMetaData().getOrigin("realm-name");
1555 switch (o)
1556 {
1557 case NotSet:
1558 {
1559 //no descriptor has set the realm-name yet, so set it
1560 context.getSecurityHandler().setRealmName(nameStr);
1561 context.getMetaData().setOrigin("realm-name", descriptor);
1562 break;
1563 }
1564 case WebXml:
1565 case WebDefaults:
1566 case WebOverride:
1567 {
1568 //set by a web xml file (web.xml/web-default.xm/web-override.xml), only allow it to be changed by another web xml file
1569 if (!(descriptor instanceof FragmentDescriptor))
1570 {
1571 context.getSecurityHandler().setRealmName(nameStr);
1572 context.getMetaData().setOrigin("realm-name", descriptor);
1573 }
1574 break;
1575 }
1576 case WebFragment:
1577 {
1578 //a fragment set it, and we must be parsing another fragment, so the values must match
1579 if (!context.getSecurityHandler().getRealmName().equals(nameStr))
1580 throw new IllegalStateException("Conflicting realm-name value in "+descriptor.getResource());
1581 break;
1582 }
1583 }
1584
1585 if (Constraint.__FORM_AUTH.equals(context.getSecurityHandler().getAuthMethod()))
1586 {
1587 XmlParser.Node formConfig = node.get("form-login-config");
1588 if (formConfig != null)
1589 {
1590 String loginPageName = null;
1591 XmlParser.Node loginPage = formConfig.get("form-login-page");
1592 if (loginPage != null)
1593 loginPageName = loginPage.toString(false, true);
1594 String errorPageName = null;
1595 XmlParser.Node errorPage = formConfig.get("form-error-page");
1596 if (errorPage != null)
1597 errorPageName = errorPage.toString(false, true);
1598
1599 //handle form-login-page
1600 o = context.getMetaData().getOrigin("form-login-page");
1601 switch (o)
1602 {
1603 case NotSet:
1604 {
1605 //Never been set before, so accept it
1606 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName);
1607 context.getMetaData().setOrigin("form-login-page",descriptor);
1608 break;
1609 }
1610 case WebXml:
1611 case WebDefaults:
1612 case WebOverride:
1613 {
1614 //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml)
1615 if (!(descriptor instanceof FragmentDescriptor))
1616 {
1617 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName);
1618 context.getMetaData().setOrigin("form-login-page",descriptor);
1619 }
1620 break;
1621 }
1622 case WebFragment:
1623 {
1624 //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree
1625 if (!context.getSecurityHandler().getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE).equals(loginPageName))
1626 throw new IllegalStateException("Conflicting form-login-page value in "+descriptor.getResource());
1627 break;
1628 }
1629 }
1630
1631 //handle form-error-page
1632 o = context.getMetaData().getOrigin("form-error-page");
1633 switch (o)
1634 {
1635 case NotSet:
1636 {
1637 //Never been set before, so accept it
1638 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName);
1639 context.getMetaData().setOrigin("form-error-page",descriptor);
1640 break;
1641 }
1642 case WebXml:
1643 case WebDefaults:
1644 case WebOverride:
1645 {
1646 //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml)
1647 if (!(descriptor instanceof FragmentDescriptor))
1648 {
1649 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName);
1650 context.getMetaData().setOrigin("form-error-page",descriptor);
1651 }
1652 break;
1653 }
1654 case WebFragment:
1655 {
1656 //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree
1657 if (!context.getSecurityHandler().getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE).equals(errorPageName))
1658 throw new IllegalStateException("Conflicting form-error-page value in "+descriptor.getResource());
1659 break;
1660 }
1661 }
1662 }
1663 else
1664 {
1665 throw new IllegalStateException("!form-login-config");
1666 }
1667 }
1668 }
1669 }
1670
1671 /**
1672 * @param context
1673 * @param descriptor
1674 * @param node
1675 */
1676 protected void visitSecurityRole(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1677 {
1678 //ServletSpec 3.0, p74 elements with multiplicity >1 are additive when merged
1679 XmlParser.Node roleNode = node.get("role-name");
1680 String role = roleNode.toString(false, true);
1681 ((ConstraintAware)context.getSecurityHandler()).addRole(role);
1682 }
1683
1684
1685 /**
1686 * @param context
1687 * @param descriptor
1688 * @param node
1689 */
1690 protected void visitFilter(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1691 {
1692 String name = node.getString("filter-name", false, true);
1693 FilterHolder holder = context.getServletHandler().getFilter(name);
1694 if (holder == null)
1695 {
1696 holder = context.getServletHandler().newFilterHolder(Holder.Source.DESCRIPTOR);
1697 holder.setName(name);
1698 context.getServletHandler().addFilter(holder);
1699 }
1700
1701 String filter_class = node.getString("filter-class", false, true);
1702 if (filter_class != null)
1703 {
1704 ((WebDescriptor)descriptor).addClassName(filter_class);
1705
1706 Origin o = context.getMetaData().getOrigin(name+".filter.filter-class");
1707 switch (o)
1708 {
1709 case NotSet:
1710 {
1711 //no class set yet
1712 holder.setClassName(filter_class);
1713 context.getMetaData().setOrigin(name+".filter.filter-class", descriptor);
1714 break;
1715 }
1716 case WebXml:
1717 case WebDefaults:
1718 case WebOverride:
1719 {
1720 //filter class was set in web.xml, only allow other web xml descriptors (override/default) to change it
1721 if (!(descriptor instanceof FragmentDescriptor))
1722 {
1723 holder.setClassName(filter_class);
1724 context.getMetaData().setOrigin(name+".filter.filter-class", descriptor);
1725 }
1726 break;
1727 }
1728 case WebFragment:
1729 {
1730 //the filter class was set up by a web fragment, all fragments must be the same
1731 if (!holder.getClassName().equals(filter_class))
1732 throw new IllegalStateException("Conflicting filter-class for filter "+name+" in "+descriptor.getResource());
1733 break;
1734 }
1735 }
1736
1737 }
1738
1739 Iterator<XmlParser.Node> iter = node.iterator("init-param");
1740 while (iter.hasNext())
1741 {
1742 XmlParser.Node paramNode = iter.next();
1743 String pname = paramNode.getString("param-name", false, true);
1744 String pvalue = paramNode.getString("param-value", false, true);
1745
1746 Origin origin = context.getMetaData().getOrigin(name+".filter.init-param."+pname);
1747 switch (origin)
1748 {
1749 case NotSet:
1750 {
1751 //init-param not already set, so set it
1752 holder.setInitParameter(pname, pvalue);
1753 context.getMetaData().setOrigin(name+".filter.init-param."+pname, descriptor);
1754 break;
1755 }
1756 case WebXml:
1757 case WebDefaults:
1758 case WebOverride:
1759 {
1760 //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
1761 //otherwise just ignore it
1762 if (!(descriptor instanceof FragmentDescriptor))
1763 {
1764 holder.setInitParameter(pname, pvalue);
1765 context.getMetaData().setOrigin(name+".filter.init-param."+pname, descriptor);
1766 }
1767 break;
1768 }
1769 case WebFragment:
1770 {
1771 //previously set by a web-fragment, make sure that the value matches, otherwise its an error
1772 if (!holder.getInitParameter(pname).equals(pvalue))
1773 throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource());
1774 break;
1775 }
1776 }
1777 }
1778
1779 String async=node.getString("async-supported",false,true);
1780 if (async!=null)
1781 holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async));
1782 if (async!=null)
1783 {
1784 boolean val = async.length()==0||Boolean.valueOf(async);
1785 Origin o = context.getMetaData().getOrigin(name+".filter.async-supported");
1786 switch (o)
1787 {
1788 case NotSet:
1789 {
1790 //set it
1791 holder.setAsyncSupported(val);
1792 context.getMetaData().setOrigin(name+".filter.async-supported", descriptor);
1793 break;
1794 }
1795 case WebXml:
1796 case WebDefaults:
1797 case WebOverride:
1798 {
1799 //async-supported set by previous web xml descriptor, only allow override if we're parsing another web descriptor(web.xml/web-override.xml/web-default.xml)
1800 if (!(descriptor instanceof FragmentDescriptor))
1801 {
1802 holder.setAsyncSupported(val);
1803 context.getMetaData().setOrigin(name+".filter.async-supported", descriptor);
1804 }
1805 break;
1806 }
1807 case WebFragment:
1808 {
1809 //async-supported set by another fragment, this fragment's value must match
1810 if (holder.isAsyncSupported() != val)
1811 throw new IllegalStateException("Conflicting async-supported="+async+" for filter "+name+" in "+descriptor.getResource());
1812 break;
1813 }
1814 }
1815 }
1816
1817 }
1818
1819 /**
1820 * @param context
1821 * @param descriptor
1822 * @param node
1823 */
1824 protected void visitFilterMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1825 {
1826 //Servlet Spec 3.0, p74
1827 //filter-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments.
1828 //Maintenance update 3.0a to spec:
1829 // Updated 8.2.3.g.v to say <servlet-mapping> elements are additive across web-fragments.
1830
1831
1832 String filter_name = node.getString("filter-name", false, true);
1833
1834 Origin origin = context.getMetaData().getOrigin(filter_name+".filter.mappings");
1835
1836 switch (origin)
1837 {
1838 case NotSet:
1839 {
1840 //no filtermappings for this filter yet defined
1841 context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
1842 addFilterMapping(filter_name, node, context, descriptor);
1843 break;
1844 }
1845 case WebDefaults:
1846 case WebOverride:
1847 case WebXml:
1848 {
1849 //filter mappings defined in a web xml file. If we're processing a fragment, we ignore filter mappings.
1850 if (!(descriptor instanceof FragmentDescriptor))
1851 {
1852 addFilterMapping(filter_name, node, context, descriptor);
1853 }
1854 break;
1855 }
1856 case WebFragment:
1857 {
1858 //filter mappings first defined in a web-fragment, allow other fragments to add
1859 addFilterMapping(filter_name, node, context, descriptor);
1860 break;
1861 }
1862 }
1863 }
1864
1865
1866 /**
1867 * @param context
1868 * @param descriptor
1869 * @param node
1870 */
1871 protected void visitListener(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1872 {
1873 String className = node.getString("listener-class", false, true);
1874 EventListener listener = null;
1875 try
1876 {
1877 if (className != null && className.length()> 0)
1878 {
1879 //Servlet Spec 3.0 p 74
1880 //Duplicate listener declarations don't result in duplicate listener instances
1881 EventListener[] listeners=context.getEventListeners();
1882 if (listeners!=null)
1883 {
1884 for (EventListener l : listeners)
1885 {
1886 if (l.getClass().getName().equals(className))
1887 return;
1888 }
1889 }
1890
1891 ((WebDescriptor)descriptor).addClassName(className);
1892
1893 Class<? extends EventListener> listenerClass = (Class<? extends EventListener>)context.loadClass(className);
1894 listener = newListenerInstance(context,listenerClass);
1895 if (!(listener instanceof EventListener))
1896 {
1897 LOG.warn("Not an EventListener: " + listener);
1898 return;
1899 }
1900 context.addEventListener(listener);
1901 context.getMetaData().setOrigin(className+".listener", descriptor);
1902
1903 }
1904 }
1905 catch (Exception e)
1906 {
1907 LOG.warn("Could not instantiate listener " + className, e);
1908 return;
1909 }
1910 }
1911
1912 /**
1913 * @param context
1914 * @param descriptor
1915 * @param node
1916 */
1917 protected void visitDistributable(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1918 {
1919 // the element has no content, so its simple presence
1920 // indicates that the webapp is distributable...
1921 //Servlet Spec 3.0 p.74 distributable only if all fragments are distributable
1922 ((WebDescriptor)descriptor).setDistributable(true);
1923 }
1924
1925 /**
1926 * @param context
1927 * @param clazz
1928 * @return the new event listener
1929 * @throws ServletException
1930 * @throws InstantiationException
1931 * @throws IllegalAccessException
1932 */
1933 protected EventListener newListenerInstance(WebAppContext context,Class<? extends EventListener> clazz) throws ServletException, InstantiationException, IllegalAccessException
1934 {
1935 try
1936 {
1937 return context.getServletContext().createListener(clazz);
1938 }
1939 catch (ServletException se)
1940 {
1941 Throwable cause = se.getRootCause();
1942 if (cause instanceof InstantiationException)
1943 throw (InstantiationException)cause;
1944 if (cause instanceof IllegalAccessException)
1945 throw (IllegalAccessException)cause;
1946 throw se;
1947 }
1948 }
1949
1950 /**
1951 * @param p
1952 * @return the normalized pattern
1953 */
1954 protected String normalizePattern(String p)
1955 {
1956 if (p != null && p.length() > 0 && !p.startsWith("/") && !p.startsWith("*")) return "/" + p;
1957 return p;
1958 }
1959
1960
1961}