1// 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} 1962