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 19 package org.eclipse.jetty.webapp; 20 21 import java.io.File; 22 import java.io.IOException; 23 import java.net.MalformedURLException; 24 import java.net.URL; 25 import java.security.PermissionCollection; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.EventListener; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 37 import javax.servlet.HttpMethodConstraintElement; 38 import javax.servlet.ServletContext; 39 import javax.servlet.ServletRegistration.Dynamic; 40 import javax.servlet.ServletSecurityElement; 41 import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic; 42 import javax.servlet.annotation.ServletSecurity.TransportGuarantee; 43 import javax.servlet.http.HttpSessionActivationListener; 44 import javax.servlet.http.HttpSessionAttributeListener; 45 import javax.servlet.http.HttpSessionBindingListener; 46 import javax.servlet.http.HttpSessionListener; 47 48 import org.eclipse.jetty.security.ConstraintAware; 49 import org.eclipse.jetty.security.ConstraintMapping; 50 import org.eclipse.jetty.security.ConstraintSecurityHandler; 51 import org.eclipse.jetty.security.SecurityHandler; 52 import org.eclipse.jetty.server.Connector; 53 import org.eclipse.jetty.server.HandlerContainer; 54 import org.eclipse.jetty.server.Server; 55 import org.eclipse.jetty.server.handler.ContextHandler; 56 import org.eclipse.jetty.server.handler.ErrorHandler; 57 import org.eclipse.jetty.server.session.SessionHandler; 58 import org.eclipse.jetty.servlet.ErrorPageErrorHandler; 59 import org.eclipse.jetty.servlet.ServletContextHandler; 60 import org.eclipse.jetty.servlet.ServletHandler; 61 import org.eclipse.jetty.util.LazyList; 62 import org.eclipse.jetty.util.Loader; 63 import org.eclipse.jetty.util.MultiException; 64 import org.eclipse.jetty.util.StringUtil; 65 import org.eclipse.jetty.util.URIUtil; 66 import org.eclipse.jetty.util.log.Log; 67 import org.eclipse.jetty.util.log.Logger; 68 import org.eclipse.jetty.util.resource.Resource; 69 import org.eclipse.jetty.util.resource.ResourceCollection; 70 import org.eclipse.jetty.util.security.Constraint; 71 72 /* ------------------------------------------------------------ */ 73 /** Web Application Context Handler. 74 * The WebAppContext handler is an extension of ContextHandler that 75 * coordinates the construction and configuration of nested handlers: 76 * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler} 77 * and {@link org.eclipse.jetty.servlet.ServletHandler}. 78 * The handlers are configured by pluggable configuration classes, with 79 * the default being {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and 80 * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}. 81 * 82 * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base" 83 * 84 */ 85 public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context 86 { 87 private static final Logger LOG = Log.getLogger(WebAppContext.class); 88 89 public static final String TEMPDIR = "javax.servlet.context.tempdir"; 90 public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir"; 91 public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml"; 92 public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page"; 93 public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration"; 94 public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses"; 95 public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses"; 96 97 private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"}; 98 99 private static String[] __dftConfigurationClasses = 100 { 101 "org.eclipse.jetty.webapp.WebInfConfiguration", 102 "org.eclipse.jetty.webapp.WebXmlConfiguration", 103 "org.eclipse.jetty.webapp.MetaInfConfiguration", 104 "org.eclipse.jetty.webapp.FragmentConfiguration", 105 "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//, 106 //"org.eclipse.jetty.webapp.TagLibConfiguration" 107 } ; 108 109 // System classes are classes that cannot be replaced by 110 // the web application, and they are *always* loaded via 111 // system classloader. 112 public final static String[] __dftSystemClasses = 113 { 114 "java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) 115 "javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) 116 "org.xml.", // needed by javax.xml 117 "org.w3c.", // needed by javax.xml 118 "org.apache.commons.logging.", // TODO: review if special case still needed 119 "org.eclipse.jetty.continuation.", // webapp cannot change continuation classes 120 "org.eclipse.jetty.jndi.", // webapp cannot change naming classes 121 "org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes 122 "org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension 123 "org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension 124 "org.eclipse.jetty.websocket.WebSocketServlet", // webapp cannot change WebSocketServlet 125 "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets 126 } ; 127 128 // Server classes are classes that are hidden from being 129 // loaded by the web application using system classloader, 130 // so if web application needs to load any of such classes, 131 // it has to include them in its distribution. 132 public final static String[] __dftServerClasses = 133 { 134 "-org.eclipse.jetty.continuation.", // don't hide continuation classes 135 "-org.eclipse.jetty.jndi.", // don't hide naming classes 136 "-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes 137 "-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension 138 "-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension 139 "-org.eclipse.jetty.websocket.WebSocketServlet", // don't hide WebSocketServlet 140 "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet 141 "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners 142 "org.eclipse.jetty." // hide other jetty classes 143 } ; 144 145 private String[] _configurationClasses = __dftConfigurationClasses; 146 private ClasspathPattern _systemClasses = null; 147 private ClasspathPattern _serverClasses = null; 148 149 private Configuration[] _configurations; 150 private String _defaultsDescriptor=WEB_DEFAULTS_XML; 151 private String _descriptor=null; 152 private final List<String> _overrideDescriptors = new ArrayList<String>(); 153 private boolean _distributable=false; 154 private boolean _extractWAR=true; 155 private boolean _copyDir=false; 156 private boolean _copyWebInf=false; // TODO change to true? 157 private boolean _logUrlOnStart =false; 158 private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority"); 159 private PermissionCollection _permissions; 160 161 private String[] _contextWhiteList = null; 162 163 private File _tmpDir; 164 private String _war; 165 private String _extraClasspath; 166 private Throwable _unavailableException; 167 168 private Map<String, String> _resourceAliases; 169 private boolean _ownClassLoader=false; 170 private boolean _configurationDiscovered=true; 171 private boolean _configurationClassesSet=false; 172 private boolean _configurationsSet=false; 173 private boolean _allowDuplicateFragmentNames = false; 174 private boolean _throwUnavailableOnStartupException = false; 175 176 177 178 private MetaData _metadata=new MetaData(); 179 getCurrentWebAppContext()180 public static WebAppContext getCurrentWebAppContext() 181 { 182 ContextHandler.Context context=ContextHandler.getCurrentContext(); 183 if (context!=null) 184 { 185 ContextHandler handler = context.getContextHandler(); 186 if (handler instanceof WebAppContext) 187 return (WebAppContext)handler; 188 } 189 return null; 190 } 191 192 /* ------------------------------------------------------------ */ WebAppContext()193 public WebAppContext() 194 { 195 super(SESSIONS|SECURITY); 196 _scontext=new Context(); 197 setErrorHandler(new ErrorPageErrorHandler()); 198 setProtectedTargets(__dftProtectedTargets); 199 } 200 201 /* ------------------------------------------------------------ */ 202 /** 203 * @param contextPath The context path 204 * @param webApp The URL or filename of the webapp directory or war file. 205 */ WebAppContext(String webApp,String contextPath)206 public WebAppContext(String webApp,String contextPath) 207 { 208 super(null,contextPath,SESSIONS|SECURITY); 209 _scontext=new Context(); 210 setContextPath(contextPath); 211 setWar(webApp); 212 setErrorHandler(new ErrorPageErrorHandler()); 213 setProtectedTargets(__dftProtectedTargets); 214 } 215 216 /* ------------------------------------------------------------ */ 217 /** 218 * @param parent The parent HandlerContainer. 219 * @param contextPath The context path 220 * @param webApp The URL or filename of the webapp directory or war file. 221 */ WebAppContext(HandlerContainer parent, String webApp, String contextPath)222 public WebAppContext(HandlerContainer parent, String webApp, String contextPath) 223 { 224 super(parent,contextPath,SESSIONS|SECURITY); 225 _scontext=new Context(); 226 setWar(webApp); 227 setErrorHandler(new ErrorPageErrorHandler()); 228 setProtectedTargets(__dftProtectedTargets); 229 } 230 231 /* ------------------------------------------------------------ */ 232 233 /** 234 * This constructor is used in the geronimo integration. 235 * 236 * @param sessionHandler SessionHandler for this web app 237 * @param securityHandler SecurityHandler for this web app 238 * @param servletHandler ServletHandler for this web app 239 * @param errorHandler ErrorHandler for this web app 240 */ WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler)241 public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) { 242 super(null, sessionHandler, securityHandler, servletHandler, errorHandler); 243 _scontext = new Context(); 244 setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler()); 245 setProtectedTargets(__dftProtectedTargets); 246 } 247 248 /* ------------------------------------------------------------ */ 249 /** 250 * @param servletContextName The servletContextName to set. 251 */ 252 @Override setDisplayName(String servletContextName)253 public void setDisplayName(String servletContextName) 254 { 255 super.setDisplayName(servletContextName); 256 ClassLoader cl = getClassLoader(); 257 if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null) 258 ((WebAppClassLoader)cl).setName(servletContextName); 259 } 260 261 /* ------------------------------------------------------------ */ 262 /** Get an exception that caused the webapp to be unavailable 263 * @return A throwable if the webapp is unavailable or null 264 */ getUnavailableException()265 public Throwable getUnavailableException() 266 { 267 return _unavailableException; 268 } 269 270 271 /* ------------------------------------------------------------ */ 272 /** Set Resource Alias. 273 * Resource aliases map resource uri's within a context. 274 * They may optionally be used by a handler when looking for 275 * a resource. 276 * @param alias 277 * @param uri 278 */ setResourceAlias(String alias, String uri)279 public void setResourceAlias(String alias, String uri) 280 { 281 if (_resourceAliases == null) 282 _resourceAliases= new HashMap<String, String>(5); 283 _resourceAliases.put(alias, uri); 284 } 285 286 /* ------------------------------------------------------------ */ getResourceAliases()287 public Map<String, String> getResourceAliases() 288 { 289 if (_resourceAliases == null) 290 return null; 291 return _resourceAliases; 292 } 293 294 /* ------------------------------------------------------------ */ setResourceAliases(Map<String, String> map)295 public void setResourceAliases(Map<String, String> map) 296 { 297 _resourceAliases = map; 298 } 299 300 /* ------------------------------------------------------------ */ getResourceAlias(String path)301 public String getResourceAlias(String path) 302 { 303 if (_resourceAliases == null) 304 return null; 305 String alias = _resourceAliases.get(path); 306 307 int slash=path.length(); 308 while (alias==null) 309 { 310 slash=path.lastIndexOf("/",slash-1); 311 if (slash<0) 312 break; 313 String match=_resourceAliases.get(path.substring(0,slash+1)); 314 if (match!=null) 315 alias=match+path.substring(slash+1); 316 } 317 return alias; 318 } 319 320 /* ------------------------------------------------------------ */ removeResourceAlias(String alias)321 public String removeResourceAlias(String alias) 322 { 323 if (_resourceAliases == null) 324 return null; 325 return _resourceAliases.remove(alias); 326 } 327 328 /* ------------------------------------------------------------ */ 329 /* (non-Javadoc) 330 * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader) 331 */ 332 @Override setClassLoader(ClassLoader classLoader)333 public void setClassLoader(ClassLoader classLoader) 334 { 335 super.setClassLoader(classLoader); 336 337 // if ( !(classLoader instanceof WebAppClassLoader) ) 338 // { 339 // LOG.info("NOTE: detected a classloader which is not an instance of WebAppClassLoader being set on WebAppContext, some typical class and resource locations may be missing on: " + toString() ); 340 // } 341 342 if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null) 343 ((WebAppClassLoader)classLoader).setName(getDisplayName()); 344 } 345 346 /* ------------------------------------------------------------ */ 347 @Override getResource(String uriInContext)348 public Resource getResource(String uriInContext) throws MalformedURLException 349 { 350 if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH)) 351 throw new MalformedURLException(uriInContext); 352 353 IOException ioe= null; 354 Resource resource= null; 355 int loop=0; 356 while (uriInContext!=null && loop++<100) 357 { 358 try 359 { 360 resource= super.getResource(uriInContext); 361 if (resource != null && resource.exists()) 362 return resource; 363 364 uriInContext = getResourceAlias(uriInContext); 365 } 366 catch (IOException e) 367 { 368 LOG.ignore(e); 369 if (ioe==null) 370 ioe= e; 371 } 372 } 373 374 if (ioe != null && ioe instanceof MalformedURLException) 375 throw (MalformedURLException)ioe; 376 377 return resource; 378 } 379 380 381 /* ------------------------------------------------------------ */ 382 /** Is the context Automatically configured. 383 * 384 * @return true if configuration discovery. 385 */ isConfigurationDiscovered()386 public boolean isConfigurationDiscovered() 387 { 388 return _configurationDiscovered; 389 } 390 391 /* ------------------------------------------------------------ */ 392 /** Set the configuration discovery mode. 393 * If configuration discovery is set to true, then the JSR315 394 * servlet 3.0 discovered configuration features are enabled. 395 * These are:<ul> 396 * <li>Web Fragments</li> 397 * <li>META-INF/resource directories</li> 398 * </ul> 399 * @param discovered true if configuration discovery is enabled for automatic configuration from the context 400 */ setConfigurationDiscovered(boolean discovered)401 public void setConfigurationDiscovered(boolean discovered) 402 { 403 _configurationDiscovered = discovered; 404 } 405 406 /* ------------------------------------------------------------ */ 407 /** Pre configure the web application. 408 * <p> 409 * The method is normally called from {@link #start()}. It performs 410 * the discovery of the configurations to be applied to this context, 411 * specifically:<ul> 412 * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}. 413 * <li>Setup the default System classes by calling {@link #loadSystemClasses()} 414 * <li>Setup the default Server classes by calling <code>loadServerClasses()</code> 415 * <li>Instantiates a classload (if one is not already set) 416 * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all 417 * Configuration instances. 418 * </ul> 419 * @throws Exception 420 */ preConfigure()421 public void preConfigure() throws Exception 422 { 423 // Setup configurations 424 loadConfigurations(); 425 426 // Setup system classes 427 loadSystemClasses(); 428 429 // Setup server classes 430 loadServerClasses(); 431 432 // Configure classloader 433 _ownClassLoader=false; 434 if (getClassLoader()==null) 435 { 436 WebAppClassLoader classLoader = new WebAppClassLoader(this); 437 setClassLoader(classLoader); 438 _ownClassLoader=true; 439 } 440 441 if (LOG.isDebugEnabled()) 442 { 443 ClassLoader loader = getClassLoader(); 444 LOG.debug("Thread Context classloader {}",loader); 445 loader=loader.getParent(); 446 while(loader!=null) 447 { 448 LOG.debug("Parent class loader: {} ",loader); 449 loader=loader.getParent(); 450 } 451 } 452 453 // Prepare for configuration 454 for (int i=0;i<_configurations.length;i++) 455 { 456 LOG.debug("preConfigure {} with {}",this,_configurations[i]); 457 _configurations[i].preConfigure(this); 458 } 459 } 460 461 /* ------------------------------------------------------------ */ configure()462 public void configure() throws Exception 463 { 464 // Configure webapp 465 for (int i=0;i<_configurations.length;i++) 466 { 467 LOG.debug("configure {} with {}",this,_configurations[i]); 468 _configurations[i].configure(this); 469 } 470 } 471 472 /* ------------------------------------------------------------ */ postConfigure()473 public void postConfigure() throws Exception 474 { 475 // Clean up after configuration 476 for (int i=0;i<_configurations.length;i++) 477 { 478 LOG.debug("postConfigure {} with {}",this,_configurations[i]); 479 _configurations[i].postConfigure(this); 480 } 481 } 482 483 /* ------------------------------------------------------------ */ 484 /* 485 * @see org.eclipse.thread.AbstractLifeCycle#doStart() 486 */ 487 @Override doStart()488 protected void doStart() throws Exception 489 { 490 try 491 { 492 _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); 493 preConfigure(); 494 super.doStart(); 495 postConfigure(); 496 497 if (isLogUrlOnStart()) 498 dumpUrl(); 499 } 500 catch (Exception e) 501 { 502 //start up of the webapp context failed, make sure it is not started 503 LOG.warn("Failed startup of context "+this, e); 504 _unavailableException=e; 505 setAvailable(false); 506 if (isThrowUnavailableOnStartupException()) 507 throw e; 508 } 509 } 510 511 /* ------------------------------------------------------------ */ 512 /* 513 * @see org.eclipse.thread.AbstractLifeCycle#doStop() 514 */ 515 @Override doStop()516 protected void doStop() throws Exception 517 { 518 super.doStop(); 519 520 try 521 { 522 for (int i=_configurations.length;i-->0;) 523 _configurations[i].deconfigure(this); 524 525 if (_metadata != null) 526 _metadata.clear(); 527 _metadata=new MetaData(); 528 529 } 530 finally 531 { 532 if (_ownClassLoader) 533 setClassLoader(null); 534 535 setAvailable(true); 536 _unavailableException=null; 537 } 538 } 539 540 /* ------------------------------------------------------------ */ 541 @Override destroy()542 public void destroy() 543 { 544 // Prepare for configuration 545 MultiException mx=new MultiException(); 546 if (_configurations!=null) 547 { 548 for (int i=_configurations.length;i-->0;) 549 { 550 try 551 { 552 _configurations[i].destroy(this); 553 } 554 catch(Exception e) 555 { 556 mx.add(e); 557 } 558 } 559 } 560 _configurations=null; 561 super.destroy(); 562 mx.ifExceptionThrowRuntime(); 563 } 564 565 566 /* ------------------------------------------------------------ */ 567 /* 568 * Dumps the current web app name and URL to the log 569 */ dumpUrl()570 private void dumpUrl() 571 { 572 Connector[] connectors = getServer().getConnectors(); 573 for (int i=0;i<connectors.length;i++) 574 { 575 String connectorName = connectors[i].getName(); 576 String displayName = getDisplayName(); 577 if (displayName == null) 578 displayName = "WebApp@"+connectors.hashCode(); 579 580 LOG.info(displayName + " at http://" + connectorName + getContextPath()); 581 } 582 } 583 584 /* ------------------------------------------------------------ */ 585 /** 586 * @return Returns the configurations. 587 */ getConfigurationClasses()588 public String[] getConfigurationClasses() 589 { 590 return _configurationClasses; 591 } 592 593 /* ------------------------------------------------------------ */ 594 /** 595 * @return Returns the configurations. 596 */ getConfigurations()597 public Configuration[] getConfigurations() 598 { 599 return _configurations; 600 } 601 602 /* ------------------------------------------------------------ */ 603 /** 604 * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml 605 * @return Returns the defaultsDescriptor. 606 */ getDefaultsDescriptor()607 public String getDefaultsDescriptor() 608 { 609 return _defaultsDescriptor; 610 } 611 612 /* ------------------------------------------------------------ */ 613 /** 614 * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml 615 * @return Returns the Override Descriptor. 616 * @deprecated use {@link #getOverrideDescriptors()} 617 */ 618 @Deprecated getOverrideDescriptor()619 public String getOverrideDescriptor() 620 { 621 if (_overrideDescriptors.size()!=1) 622 return null; 623 return _overrideDescriptors.get(0); 624 } 625 626 /* ------------------------------------------------------------ */ 627 /** 628 * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml 629 * @return Returns the Override Descriptor list 630 */ getOverrideDescriptors()631 public List<String> getOverrideDescriptors() 632 { 633 return Collections.unmodifiableList(_overrideDescriptors); 634 } 635 636 /* ------------------------------------------------------------ */ 637 /** 638 * @return Returns the permissions. 639 */ getPermissions()640 public PermissionCollection getPermissions() 641 { 642 return _permissions; 643 } 644 645 /* ------------------------------------------------------------ */ 646 /** 647 * @see #setServerClasses(String[]) 648 * @return Returns the serverClasses. 649 */ getServerClasses()650 public String[] getServerClasses() 651 { 652 if (_serverClasses == null) 653 loadServerClasses(); 654 655 return _serverClasses.getPatterns(); 656 } 657 addServerClass(String classname)658 public void addServerClass(String classname) 659 { 660 if (_serverClasses == null) 661 loadServerClasses(); 662 663 _serverClasses.addPattern(classname); 664 } 665 666 /* ------------------------------------------------------------ */ 667 /** 668 * @see #setSystemClasses(String[]) 669 * @return Returns the systemClasses. 670 */ getSystemClasses()671 public String[] getSystemClasses() 672 { 673 if (_systemClasses == null) 674 loadSystemClasses(); 675 676 return _systemClasses.getPatterns(); 677 } 678 679 /* ------------------------------------------------------------ */ addSystemClass(String classname)680 public void addSystemClass(String classname) 681 { 682 if (_systemClasses == null) 683 loadSystemClasses(); 684 685 _systemClasses.addPattern(classname); 686 } 687 688 /* ------------------------------------------------------------ */ isServerClass(String name)689 public boolean isServerClass(String name) 690 { 691 if (_serverClasses == null) 692 loadServerClasses(); 693 694 return _serverClasses.match(name); 695 } 696 697 /* ------------------------------------------------------------ */ isSystemClass(String name)698 public boolean isSystemClass(String name) 699 { 700 if (_systemClasses == null) 701 loadSystemClasses(); 702 703 return _systemClasses.match(name); 704 } 705 706 /* ------------------------------------------------------------ */ loadSystemClasses()707 protected void loadSystemClasses() 708 { 709 if (_systemClasses != null) 710 return; 711 712 //look for a Server attribute with the list of System classes 713 //to apply to every web application. If not present, use our defaults. 714 Server server = getServer(); 715 if (server != null) 716 { 717 Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES); 718 if (systemClasses != null && systemClasses instanceof String[]) 719 _systemClasses = new ClasspathPattern((String[])systemClasses); 720 } 721 722 if (_systemClasses == null) 723 _systemClasses = new ClasspathPattern(__dftSystemClasses); 724 } 725 726 /* ------------------------------------------------------------ */ loadServerClasses()727 private void loadServerClasses() 728 { 729 if (_serverClasses != null) 730 { 731 return; 732 } 733 734 // look for a Server attribute with the list of Server classes 735 // to apply to every web application. If not present, use our defaults. 736 Server server = getServer(); 737 if (server != null) 738 { 739 Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES); 740 if (serverClasses != null && serverClasses instanceof String[]) 741 { 742 _serverClasses = new ClasspathPattern((String[])serverClasses); 743 } 744 } 745 746 if (_serverClasses == null) 747 { 748 _serverClasses = new ClasspathPattern(__dftServerClasses); 749 } 750 } 751 752 /* ------------------------------------------------------------ */ 753 /** 754 * @return Returns the war as a file or URL string (Resource) 755 */ getWar()756 public String getWar() 757 { 758 if (_war==null) 759 _war=getResourceBase(); 760 return _war; 761 } 762 763 /* ------------------------------------------------------------ */ getWebInf()764 public Resource getWebInf() throws IOException 765 { 766 if (super.getBaseResource() == null) 767 return null; 768 769 // Iw there a WEB-INF directory? 770 Resource web_inf= super.getBaseResource().addPath("WEB-INF/"); 771 if (!web_inf.exists() || !web_inf.isDirectory()) 772 return null; 773 774 return web_inf; 775 } 776 777 /* ------------------------------------------------------------ */ 778 /** 779 * @return Returns the distributable. 780 */ isDistributable()781 public boolean isDistributable() 782 { 783 return _distributable; 784 } 785 786 /* ------------------------------------------------------------ */ 787 /** 788 * @return Returns the extractWAR. 789 */ isExtractWAR()790 public boolean isExtractWAR() 791 { 792 return _extractWAR; 793 } 794 795 /* ------------------------------------------------------------ */ 796 /** 797 * @return True if the webdir is copied (to allow hot replacement of jars on windows) 798 */ isCopyWebDir()799 public boolean isCopyWebDir() 800 { 801 return _copyDir; 802 } 803 804 /* ------------------------------------------------------------ */ 805 /** 806 * @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows) 807 */ isCopyWebInf()808 public boolean isCopyWebInf() 809 { 810 return _copyWebInf; 811 } 812 813 /* ------------------------------------------------------------ */ 814 /** 815 * @return True if the classloader should delegate first to the parent 816 * classloader (standard java behaviour) or false if the classloader 817 * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet 818 * spec recommendation). 819 */ isParentLoaderPriority()820 public boolean isParentLoaderPriority() 821 { 822 return _parentLoaderPriority; 823 } 824 825 826 /* ------------------------------------------------------------ */ getDefaultConfigurationClasses()827 public String[] getDefaultConfigurationClasses () 828 { 829 return __dftConfigurationClasses; 830 } 831 832 /* ------------------------------------------------------------ */ getDefaultServerClasses()833 public String[] getDefaultServerClasses () 834 { 835 return __dftServerClasses; 836 } 837 838 /* ------------------------------------------------------------ */ getDefaultSystemClasses()839 public String[] getDefaultSystemClasses () 840 { 841 return __dftSystemClasses; 842 } 843 844 /* ------------------------------------------------------------ */ loadConfigurations()845 protected void loadConfigurations() 846 throws Exception 847 { 848 //if the configuration instances have been set explicitly, use them 849 if (_configurations!=null) 850 return; 851 852 //if the configuration classnames have been set explicitly use them 853 if (!_configurationClassesSet) 854 _configurationClasses=__dftConfigurationClasses; 855 856 _configurations = new Configuration[_configurationClasses.length]; 857 for (int i = 0; i < _configurationClasses.length; i++) 858 { 859 _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance(); 860 } 861 } 862 863 864 865 /* ------------------------------------------------------------ */ 866 @Override toString()867 public String toString() 868 { 869 return super.toString()+(_war==null?"":(","+_war)); 870 } 871 872 /* ------------------------------------------------------------ */ 873 /** 874 * @param configurations The configuration class names. If setConfigurations is not called 875 * these classes are used to create a configurations array. 876 */ setConfigurationClasses(String[] configurations)877 public void setConfigurationClasses(String[] configurations) 878 { 879 if (isRunning()) 880 throw new IllegalStateException(); 881 _configurationClasses = configurations==null?null:(String[])configurations.clone(); 882 _configurationClassesSet = true; 883 _configurations=null; 884 } 885 886 /* ------------------------------------------------------------ */ 887 /** 888 * @param configurations The configurations to set. 889 */ setConfigurations(Configuration[] configurations)890 public void setConfigurations(Configuration[] configurations) 891 { 892 if (isRunning()) 893 throw new IllegalStateException(); 894 _configurations = configurations==null?null:(Configuration[])configurations.clone(); 895 _configurationsSet = true; 896 } 897 898 /* ------------------------------------------------------------ */ 899 /** 900 * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml 901 * @param defaultsDescriptor The defaultsDescriptor to set. 902 */ setDefaultsDescriptor(String defaultsDescriptor)903 public void setDefaultsDescriptor(String defaultsDescriptor) 904 { 905 _defaultsDescriptor = defaultsDescriptor; 906 } 907 908 /* ------------------------------------------------------------ */ 909 /** 910 * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml 911 * @param overrideDescriptor The overrideDescritpor to set. 912 * @deprecated use {@link #setOverrideDescriptors(List)} 913 */ 914 @Deprecated setOverrideDescriptor(String overrideDescriptor)915 public void setOverrideDescriptor(String overrideDescriptor) 916 { 917 _overrideDescriptors.clear(); 918 _overrideDescriptors.add(overrideDescriptor); 919 } 920 921 /* ------------------------------------------------------------ */ 922 /** 923 * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml 924 * @param overrideDescriptors The overrideDescriptors (file or URL) to set. 925 */ setOverrideDescriptors(List<String> overrideDescriptors)926 public void setOverrideDescriptors(List<String> overrideDescriptors) 927 { 928 _overrideDescriptors.clear(); 929 _overrideDescriptors.addAll(overrideDescriptors); 930 } 931 932 /* ------------------------------------------------------------ */ 933 /** 934 * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml 935 * @param overrideDescriptor The overrideDescriptor (file or URL) to add. 936 */ addOverrideDescriptor(String overrideDescriptor)937 public void addOverrideDescriptor(String overrideDescriptor) 938 { 939 _overrideDescriptors.add(overrideDescriptor); 940 } 941 942 /* ------------------------------------------------------------ */ 943 /** 944 * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists. 945 */ getDescriptor()946 public String getDescriptor() 947 { 948 return _descriptor; 949 } 950 951 /* ------------------------------------------------------------ */ 952 /** 953 * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists. 954 */ setDescriptor(String descriptor)955 public void setDescriptor(String descriptor) 956 { 957 _descriptor=descriptor; 958 } 959 960 /* ------------------------------------------------------------ */ 961 /** 962 * @param distributable The distributable to set. 963 */ setDistributable(boolean distributable)964 public void setDistributable(boolean distributable) 965 { 966 this._distributable = distributable; 967 } 968 969 /* ------------------------------------------------------------ */ 970 @Override setEventListeners(EventListener[] eventListeners)971 public void setEventListeners(EventListener[] eventListeners) 972 { 973 if (_sessionHandler!=null) 974 _sessionHandler.clearEventListeners(); 975 976 super.setEventListeners(eventListeners); 977 978 for (int i=0; eventListeners!=null && i<eventListeners.length;i ++) 979 { 980 EventListener listener = eventListeners[i]; 981 982 if ((listener instanceof HttpSessionActivationListener) 983 || (listener instanceof HttpSessionAttributeListener) 984 || (listener instanceof HttpSessionBindingListener) 985 || (listener instanceof HttpSessionListener)) 986 { 987 if (_sessionHandler!=null) 988 _sessionHandler.addEventListener(listener); 989 } 990 991 } 992 } 993 994 995 996 /* ------------------------------------------------------------ */ 997 /** 998 * @param extractWAR True if war files are extracted 999 */ setExtractWAR(boolean extractWAR)1000 public void setExtractWAR(boolean extractWAR) 1001 { 1002 _extractWAR = extractWAR; 1003 } 1004 1005 /* ------------------------------------------------------------ */ 1006 /** 1007 * @param copy True if the webdir is copied (to allow hot replacement of jars) 1008 */ setCopyWebDir(boolean copy)1009 public void setCopyWebDir(boolean copy) 1010 { 1011 _copyDir = copy; 1012 } 1013 1014 /* ------------------------------------------------------------ */ 1015 /** 1016 * @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows) 1017 */ setCopyWebInf(boolean copyWebInf)1018 public void setCopyWebInf(boolean copyWebInf) 1019 { 1020 _copyWebInf = copyWebInf; 1021 } 1022 1023 /* ------------------------------------------------------------ */ 1024 /** 1025 * @param java2compliant The java2compliant to set. 1026 */ setParentLoaderPriority(boolean java2compliant)1027 public void setParentLoaderPriority(boolean java2compliant) 1028 { 1029 _parentLoaderPriority = java2compliant; 1030 } 1031 1032 /* ------------------------------------------------------------ */ 1033 /** 1034 * @param permissions The permissions to set. 1035 */ setPermissions(PermissionCollection permissions)1036 public void setPermissions(PermissionCollection permissions) 1037 { 1038 _permissions = permissions; 1039 } 1040 1041 /** 1042 * Set the context white list 1043 * 1044 * In certain circumstances you want may want to deny access of one webapp from another 1045 * when you may not fully trust the webapp. Setting this white list will enable a 1046 * check when a servlet called getContext(String), validating that the uriInPath 1047 * for the given webapp has been declaratively allows access to the context. 1048 * @param contextWhiteList 1049 */ setContextWhiteList(String[] contextWhiteList)1050 public void setContextWhiteList(String[] contextWhiteList) 1051 { 1052 _contextWhiteList = contextWhiteList; 1053 } 1054 1055 /* ------------------------------------------------------------ */ 1056 /** 1057 * Set the server classes patterns. 1058 * <p> 1059 * Server classes/packages are classes used to implement the server and are hidden 1060 * from the context. If the context needs to load these classes, it must have its 1061 * own copy of them in WEB-INF/lib or WEB-INF/classes. 1062 * A class pattern is a string of one of the forms:<dl> 1063 * <dt>org.package.Classname</dt><dd>Match a specific class</dd> 1064 * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd> 1065 * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd> 1066 * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd> 1067 * </dl> 1068 * @param serverClasses The serverClasses to set. 1069 */ setServerClasses(String[] serverClasses)1070 public void setServerClasses(String[] serverClasses) 1071 { 1072 _serverClasses = new ClasspathPattern(serverClasses); 1073 } 1074 1075 /* ------------------------------------------------------------ */ 1076 /** 1077 * Set the system classes patterns. 1078 * <p> 1079 * System classes/packages are classes provided by the JVM and that 1080 * cannot be replaced by classes of the same name from WEB-INF, 1081 * regardless of the value of {@link #setParentLoaderPriority(boolean)}. 1082 * A class pattern is a string of one of the forms:<dl> 1083 * <dt>org.package.Classname</dt><dd>Match a specific class</dd> 1084 * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd> 1085 * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd> 1086 * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd> 1087 * </dl> 1088 * @param systemClasses The systemClasses to set. 1089 */ setSystemClasses(String[] systemClasses)1090 public void setSystemClasses(String[] systemClasses) 1091 { 1092 _systemClasses = new ClasspathPattern(systemClasses); 1093 } 1094 1095 1096 /* ------------------------------------------------------------ */ 1097 /** Set temporary directory for context. 1098 * The javax.servlet.context.tempdir attribute is also set. 1099 * @param dir Writable temporary directory. 1100 */ setTempDirectory(File dir)1101 public void setTempDirectory(File dir) 1102 { 1103 if (isStarted()) 1104 throw new IllegalStateException("Started"); 1105 1106 if (dir!=null) 1107 { 1108 try{dir=new File(dir.getCanonicalPath());} 1109 catch (IOException e){LOG.warn(Log.EXCEPTION,e);} 1110 } 1111 1112 if (dir!=null && !dir.exists()) 1113 { 1114 dir.mkdir(); 1115 dir.deleteOnExit(); 1116 } 1117 1118 if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite())) 1119 throw new IllegalArgumentException("Bad temp directory: "+dir); 1120 1121 try 1122 { 1123 if (dir!=null) 1124 dir=dir.getCanonicalFile(); 1125 } 1126 catch(Exception e) 1127 { 1128 LOG.warn(e); 1129 } 1130 _tmpDir=dir; 1131 setAttribute(TEMPDIR,_tmpDir); 1132 } 1133 1134 /* ------------------------------------------------------------ */ getTempDirectory()1135 public File getTempDirectory () 1136 { 1137 return _tmpDir; 1138 } 1139 1140 /* ------------------------------------------------------------ */ 1141 /** 1142 * @param war The war to set as a file name or URL 1143 */ setWar(String war)1144 public void setWar(String war) 1145 { 1146 _war = war; 1147 } 1148 1149 /* ------------------------------------------------------------ */ 1150 /** 1151 * @return Comma or semicolon separated path of filenames or URLs 1152 * pointing to directories or jar files. Directories should end 1153 * with '/'. 1154 */ getExtraClasspath()1155 public String getExtraClasspath() 1156 { 1157 return _extraClasspath; 1158 } 1159 1160 /* ------------------------------------------------------------ */ 1161 /** 1162 * @param extraClasspath Comma or semicolon separated path of filenames or URLs 1163 * pointing to directories or jar files. Directories should end 1164 * with '/'. 1165 */ setExtraClasspath(String extraClasspath)1166 public void setExtraClasspath(String extraClasspath) 1167 { 1168 _extraClasspath=extraClasspath; 1169 } 1170 1171 /* ------------------------------------------------------------ */ isLogUrlOnStart()1172 public boolean isLogUrlOnStart() 1173 { 1174 return _logUrlOnStart; 1175 } 1176 1177 /* ------------------------------------------------------------ */ 1178 /** 1179 * Sets whether or not the web app name and URL is logged on startup 1180 * 1181 * @param logOnStart whether or not the log message is created 1182 */ setLogUrlOnStart(boolean logOnStart)1183 public void setLogUrlOnStart(boolean logOnStart) 1184 { 1185 this._logUrlOnStart = logOnStart; 1186 } 1187 1188 1189 /* ------------------------------------------------------------ */ 1190 @Override setServer(Server server)1191 public void setServer(Server server) 1192 { 1193 super.setServer(server); 1194 //if we haven't been given a set of configuration instances to 1195 //use, and we haven't been given a set of configuration classes 1196 //to use, use the configuration classes that came from the 1197 //Server (if there are any) 1198 if (!_configurationsSet && !_configurationClassesSet && server != null) 1199 { 1200 String[] serverConfigs = (String[])server.getAttribute(SERVER_CONFIG); 1201 if (serverConfigs != null) 1202 setConfigurationClasses(serverConfigs); 1203 } 1204 } 1205 1206 1207 /* ------------------------------------------------------------ */ isAllowDuplicateFragmentNames()1208 public boolean isAllowDuplicateFragmentNames() 1209 { 1210 return _allowDuplicateFragmentNames; 1211 } 1212 1213 1214 /* ------------------------------------------------------------ */ setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)1215 public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames) 1216 { 1217 _allowDuplicateFragmentNames = allowDuplicateFragmentNames; 1218 } 1219 1220 1221 /* ------------------------------------------------------------ */ setThrowUnavailableOnStartupException(boolean throwIfStartupException)1222 public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) { 1223 _throwUnavailableOnStartupException = throwIfStartupException; 1224 } 1225 1226 1227 /* ------------------------------------------------------------ */ isThrowUnavailableOnStartupException()1228 public boolean isThrowUnavailableOnStartupException () { 1229 return _throwUnavailableOnStartupException; 1230 } 1231 1232 /* ------------------------------------------------------------ */ 1233 @Override startContext()1234 protected void startContext() 1235 throws Exception 1236 { 1237 configure(); 1238 1239 //resolve the metadata 1240 _metadata.resolve(this); 1241 1242 super.startContext(); 1243 } 1244 1245 /* ------------------------------------------------------------ */ 1246 @Override setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)1247 public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement) 1248 { 1249 1250 Set<String> unchangedURLMappings = new HashSet<String>(); 1251 //From javadoc for ServletSecurityElement: 1252 /* 1253 If a URL pattern of this ServletRegistration is an exact target of a security-constraint that 1254 was established via the portable deployment descriptor, then this method does not change the 1255 security-constraint for that pattern, and the pattern will be included in the return value. 1256 1257 If a URL pattern of this ServletRegistration is an exact target of a security constraint 1258 that was established via the ServletSecurity annotation or a previous call to this method, 1259 then this method replaces the security constraint for that pattern. 1260 1261 If a URL pattern of this ServletRegistration is neither the exact target of a security constraint 1262 that was established via the ServletSecurity annotation or a previous call to this method, 1263 nor the exact target of a security-constraint in the portable deployment descriptor, then 1264 this method establishes the security constraint for that pattern from the argument ServletSecurityElement. 1265 */ 1266 1267 Collection<String> pathMappings = registration.getMappings(); 1268 if (pathMappings != null) 1269 { 1270 Constraint constraint = ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement); 1271 1272 for (String pathSpec:pathMappings) 1273 { 1274 Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec); 1275 1276 switch (origin) 1277 { 1278 case NotSet: 1279 { 1280 //No mapping for this url already established 1281 List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement); 1282 for (ConstraintMapping m:mappings) 1283 ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m); 1284 getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API); 1285 break; 1286 } 1287 case WebXml: 1288 case WebDefaults: 1289 case WebOverride: 1290 case WebFragment: 1291 { 1292 //a mapping for this url was created in a descriptor, which overrides everything 1293 unchangedURLMappings.add(pathSpec); 1294 break; 1295 } 1296 case Annotation: 1297 case API: 1298 { 1299 //mapping established via an annotation or by previous call to this method, 1300 //replace the security constraint for this pattern 1301 List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings()); 1302 1303 List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement); 1304 constraintMappings.addAll(freshMappings); 1305 1306 ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings); 1307 break; 1308 } 1309 } 1310 } 1311 } 1312 1313 return unchangedURLMappings; 1314 } 1315 1316 1317 1318 /* ------------------------------------------------------------ */ 1319 public class Context extends ServletContextHandler.Context 1320 { 1321 /* ------------------------------------------------------------ */ 1322 @Override getResource(String path)1323 public URL getResource(String path) throws MalformedURLException 1324 { 1325 Resource resource=WebAppContext.this.getResource(path); 1326 if (resource==null || !resource.exists()) 1327 return null; 1328 1329 // Should we go to the original war? 1330 if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR()) 1331 { 1332 Resource[] resources = ((ResourceCollection)resource).getResources(); 1333 for (int i=resources.length;i-->0;) 1334 { 1335 if (resources[i].getName().startsWith("jar:file")) 1336 return resources[i].getURL(); 1337 } 1338 } 1339 1340 return resource.getURL(); 1341 } 1342 1343 /* ------------------------------------------------------------ */ 1344 @Override getContext(String uripath)1345 public ServletContext getContext(String uripath) 1346 { 1347 ServletContext servletContext = super.getContext(uripath); 1348 1349 if ( servletContext != null && _contextWhiteList != null ) 1350 { 1351 for ( String context : _contextWhiteList ) 1352 { 1353 if ( context.equals(uripath) ) 1354 { 1355 return servletContext; 1356 } 1357 } 1358 1359 return null; 1360 } 1361 else 1362 { 1363 return servletContext; 1364 } 1365 } 1366 1367 1368 1369 } 1370 1371 /* ------------------------------------------------------------ */ getMetaData()1372 public MetaData getMetaData() 1373 { 1374 return _metadata; 1375 } 1376 1377 } 1378