Source code: org/mortbay/jetty/servlet/WebApplicationContext.java
1 // ========================================================================
2 // Copyright (c) 2000 Mort Bay Consulting (Australia) Pty. Ltd.
3 // $Id: WebApplicationContext.java,v 1.113 2003/11/22 16:06:03 gregwilkins Exp $
4 // ========================================================================
5
6 package org.mortbay.jetty.servlet;
7
8 import java.io.Externalizable;
9 import java.io.File;
10 import java.io.IOException;
11 import java.net.MalformedURLException;
12 import java.net.URL;
13 import java.security.PermissionCollection;
14 import java.util.EventListener;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.Map;
19 import java.util.Set;
20
21 import javax.servlet.ServletContextAttributeEvent;
22 import javax.servlet.ServletContextAttributeListener;
23 import javax.servlet.ServletContextEvent;
24 import javax.servlet.ServletContextListener;
25 import javax.servlet.UnavailableException;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.mortbay.http.Authenticator;
30 import org.mortbay.http.BasicAuthenticator;
31 import org.mortbay.http.ClientCertAuthenticator;
32 import org.mortbay.http.DigestAuthenticator;
33 import org.mortbay.http.HttpException;
34 import org.mortbay.http.HttpHandler;
35 import org.mortbay.http.HttpRequest;
36 import org.mortbay.http.HttpResponse;
37 import org.mortbay.http.SecurityConstraint;
38 import org.mortbay.http.UserRealm;
39 import org.mortbay.util.JarResource;
40 import org.mortbay.util.LazyList;
41 import org.mortbay.util.LogSupport;
42 import org.mortbay.util.MultiException;
43 import org.mortbay.util.Resource;
44 import org.mortbay.xml.XmlConfiguration;
45 import org.mortbay.xml.XmlParser;
46
47
48 /* ------------------------------------------------------------ */
49 /** Standard web.xml configured HttpContext.
50 *
51 * This specialization of HttpContext uses the standardized web.xml
52 * to describe a web application and configure the handlers for the
53 * HttpContext.
54 *
55 * If a file named web-jetty.xml or jetty-web.xml is found in the
56 * WEB-INF directory it is applied to the context using the
57 * XmlConfiguration format.
58 *
59 * A single WebApplicationHandler instance is used to provide
60 * security, filter, sevlet and resource handling.
61 *
62 * @see org.mortbay.jetty.servlet.WebApplicationHandler
63 * @version $Id: WebApplicationContext.java,v 1.113 2003/11/22 16:06:03 gregwilkins Exp $
64 * @author Greg Wilkins (gregw)
65 */
66 public class WebApplicationContext
67 extends ServletHttpContext
68 implements Externalizable
69 {
70 private static Log log = LogFactory.getLog(WebApplicationContext.class);
71
72 /* ------------------------------------------------------------ */
73 private String _deploymentDescriptor;
74 private String _defaultsDescriptor="org/mortbay/jetty/servlet/webdefault.xml";
75 private String _war;
76 private boolean _extract;
77 private boolean _ignorewebjetty;
78
79 private transient String _name;
80 private transient FormAuthenticator _formAuthenticator;
81 private transient Map _resourceAliases;
82 private transient Resource _webApp;
83 private transient Resource _webInf;
84 private transient Set _warnings;
85 private transient WebApplicationHandler _webAppHandler;
86 private transient Map _tagLibMap;
87 private transient Object _contextListeners;
88 private transient Object _contextAttributeListeners;
89 private transient Map _errorPages;
90
91
92 /* ------------------------------------------------------------ */
93 /** Constructor.
94 */
95 public WebApplicationContext()
96 {}
97
98 /* ------------------------------------------------------------ */
99 /** Constructor.
100 * @param webApp The Web application directory or WAR file.
101 */
102 public WebApplicationContext(String webApp)
103 {
104 _war=webApp;
105 }
106
107 /* ------------------------------------------------------------ */
108 public void writeExternal(java.io.ObjectOutput out)
109 throws java.io.IOException
110 {
111 out.writeObject(getContextPath());
112 out.writeObject(getVirtualHosts());
113 HttpHandler[] handlers = getHandlers();
114 for (int i=0;i<handlers.length;i++)
115 {
116 if (handlers[i] instanceof WebApplicationHandler)
117 break;
118 out.writeObject(handlers[i]);
119 }
120 out.writeObject(getAttributes());
121 out.writeBoolean(isRedirectNullPath());
122 out.writeInt(getMaxCachedFileSize());
123 out.writeInt(getMaxCacheSize());
124 out.writeBoolean(getStatsOn());
125 out.writeObject(getPermissions());
126 out.writeBoolean(isClassLoaderJava2Compliant());
127
128 out.writeObject(_deploymentDescriptor);
129 out.writeObject(_defaultsDescriptor);
130 out.writeObject(_war);
131 out.writeBoolean(_extract);
132 out.writeBoolean(_ignorewebjetty);
133 }
134
135 /* ------------------------------------------------------------ */
136 public void readExternal(java.io.ObjectInput in)
137 throws java.io.IOException, ClassNotFoundException
138 {
139 setContextPath((String)in.readObject());
140 setVirtualHosts((String[])in.readObject());
141 Object o = in.readObject();
142
143 while(o instanceof HttpHandler)
144 {
145 addHandler((HttpHandler)o);
146 o = in.readObject();
147 }
148 setAttributes((Map)o);
149 setRedirectNullPath(in.readBoolean());
150 setMaxCachedFileSize(in.readInt());
151 setMaxCacheSize(in.readInt());
152 setStatsOn(in.readBoolean());
153 setPermissions((PermissionCollection)in.readObject());
154 setClassLoaderJava2Compliant(in.readBoolean());
155
156 _deploymentDescriptor=(String)in.readObject();
157 _defaultsDescriptor=(String)in.readObject();
158 _war=(String)in.readObject();
159 _extract=in.readBoolean();
160 _ignorewebjetty=in.readBoolean();
161 }
162
163 /* ------------------------------------------------------------ */
164 /**
165 * @param war Filename or URL of the web application directory or WAR file.
166 */
167 public void setWAR(String war)
168 {
169 _war=war;
170 }
171
172 /* ------------------------------------------------------------ */
173 public String getWAR()
174 {
175 return _war;
176 }
177
178 /* ------------------------------------------------------------ */
179 public WebApplicationHandler getWebApplicationHandler()
180 {
181 if (_webAppHandler==null)
182 getServletHandler();
183 return _webAppHandler;
184 }
185
186 /* ------------------------------------------------------------ */
187 private void resolveWebApp()
188 throws IOException
189 {
190 if (_webApp==null && _war!=null && _war.length()>0)
191 {
192 // Set dir or WAR
193 _webApp = Resource.newResource(_war);
194
195
196 // Accept aliases for WAR files
197 if (_webApp.getAlias()!=null)
198 {
199 log.info(_webApp+" anti-aliased to "+_webApp.getAlias());
200 _webApp= Resource.newResource(_webApp.getAlias());
201 }
202
203 if(log.isDebugEnabled())log.debug("Try webapp="+_webApp+
204 ", exists="+_webApp.exists()+
205 ", directory="+_webApp.isDirectory());
206
207 // Is the WAR usable directly?
208 if (_webApp.exists() &&
209 !_webApp.isDirectory() &&
210 !_webApp.toString().startsWith("jar:"))
211 {
212 // No - then lets see if it can be turned into a jar URL.
213 Resource jarWebApp = Resource.newResource("jar:"+_webApp+"!/");
214 if (jarWebApp.exists() && jarWebApp.isDirectory())
215 {
216 _webApp=jarWebApp;
217 _war=_webApp.toString();
218 if(log.isDebugEnabled())log.debug("Try webapp="+_webApp+
219 ", exists="+_webApp.exists()+
220 ", directory="+_webApp.isDirectory());
221 }
222 }
223
224 // If we should extract or the URL is still not usable
225 if (_webApp.exists() &&
226 (!_webApp.isDirectory() ||
227 ( _extract && _webApp.getFile()==null) ||
228 ( _extract && _webApp.getFile()!=null && !_webApp.getFile().isDirectory())))
229 {
230 // Then extract it.
231 File tempDir=new File(getTempDirectory(),"webapp");
232 if (tempDir.exists())
233 tempDir.delete();
234 tempDir.mkdir();
235 tempDir.deleteOnExit();
236 log.info("Extract "+_war+" to "+tempDir);
237 JarResource.extract(_webApp,tempDir,true);
238 _webApp=Resource.newResource(tempDir.getCanonicalPath());
239
240 if(log.isDebugEnabled())log.debug("Try webapp="+_webApp+
241 ", exists="+_webApp.exists()+
242 ", directory="+_webApp.isDirectory());
243 }
244
245 // Now do we have something usable?
246 if (!_webApp.exists() || !_webApp.isDirectory())
247 {
248 log.warn("Web application not found "+_war);
249 throw new java.io.FileNotFoundException(_war);
250 }
251
252 if(log.isDebugEnabled())log.debug("webapp="+_webApp);
253
254 // Iw there a WEB-INF directory?
255 _webInf = _webApp.addPath("WEB-INF/");
256 if (!_webInf.exists() || !_webInf.isDirectory())
257 _webInf=null;
258 else
259 {
260 // Is there a WEB-INF work directory
261 Resource work=_webInf.addPath("work");
262 if (work.exists() && work.isDirectory() &&
263 work.getFile()!=null && work.getFile().canWrite() &&
264 getAttribute("javax.servlet.context.tempdir")==null)
265 setAttribute("javax.servlet.context.tempdir",work.getFile());
266 }
267
268
269 // ResourcePath
270 super.setBaseResource(_webApp);
271 }
272 }
273
274 /* ------------------------------------------------------------ */
275 /** Get the context ServletHandler.
276 * Conveniance method. If no ServletHandler exists, a new one is added to
277 * the context. This derivation of the method creates a
278 * WebApplicationHandler extension of ServletHandler.
279 * @return WebApplicationHandler
280 */
281 public synchronized ServletHandler getServletHandler()
282 {
283 if (_webAppHandler==null)
284 {
285 _webAppHandler=(WebApplicationHandler)getHandler(WebApplicationHandler.class);
286 if (_webAppHandler==null)
287 {
288 if (getHandler(ServletHandler.class)!=null)
289 throw new IllegalStateException("Cannot have ServletHandler in WebApplicationContext");
290 _webAppHandler=new WebApplicationHandler();
291 addHandler(_webAppHandler);
292 }
293 }
294 return _webAppHandler;
295 }
296
297 /* ------------------------------------------------------------ */
298 public void setPermissions(PermissionCollection permissions)
299 {
300 if (!_ignorewebjetty)
301 log.warn("Permissions set with web-jetty.xml enabled");
302 super.setPermissions(permissions);
303 }
304
305 /* ------------------------------------------------------------ */
306 public boolean isIgnoreWebJetty()
307 {
308 return _ignorewebjetty;
309 }
310
311 /* ------------------------------------------------------------ */
312 /**
313 * @param b If TRUE, web-jetty.xml and jetty-web.xml configuration
314 * files are ignored.
315 */
316 public void setIgnoreWebJetty(boolean b)
317 {
318 _ignorewebjetty=b;
319 if (b && getPermissions()!=null)
320 log.warn("Permissions set with web-jetty.xml enabled");
321 }
322
323 /* ------------------------------------------------------------ */
324 /** Start the Web Application.
325 * @exception IOException
326 */
327 public void start()
328 throws Exception
329 {
330 if (isStarted())
331 return;
332
333 setWelcomeFiles(null);
334 _tagLibMap=new HashMap(3);
335
336 // save context classloader
337 Thread thread = Thread.currentThread();
338 ClassLoader lastContextLoader=thread.getContextClassLoader();
339
340 MultiException mex=null;
341 try
342 {
343 // Get parser
344 XmlParser xmlParser=new XmlParser();
345
346 //set up cache of DTDs and schemas locally
347 URL dtd22=WebApplicationContext.class
348 .getResource("/javax/servlet/resources/web-app_2_2.dtd");
349 URL dtd23=WebApplicationContext.class
350 .getResource("/javax/servlet/resources/web-app_2_3.dtd");
351 URL jsp20xsd = WebApplicationContext.class
352 .getResource("/javax/servlet/resources/jsp_2_0.xsd");
353 URL j2ee14xsd = WebApplicationContext.class
354 .getResource("/javax/servlet/resources/j2ee_1_4.xsd");
355 URL webapp24xsd = WebApplicationContext.class
356 .getResource ("/javax/servlet/resources/web-app_2_4.xsd");
357 URL schemadtd = WebApplicationContext.class
358 .getResource ("/javax/servlet/resources/XMLSchema.dtd");
359 URL xmlxsd = WebApplicationContext.class
360 .getResource ("/javax/servlet/resources/xml.xsd");
361 URL webservice11xsd = WebApplicationContext.class
362 .getResource ("/javax/servlet/resources/j2ee_web_services_client_1_1.xsd");
363 URL datatypesdtd = WebApplicationContext.class
364 .getResource ("/javax/servlet/resources/datatypes.dtd");
365
366 xmlParser.redirectEntity("web-app_2_2.dtd",dtd22);
367 xmlParser.redirectEntity("-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",dtd22);
368
369 xmlParser.redirectEntity("web.dtd",dtd23);
370 xmlParser.redirectEntity("web-app_2_3.dtd",dtd23);
371 xmlParser.redirectEntity("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",dtd23);
372
373 xmlParser.redirectEntity("XMLSchema.dtd", schemadtd);
374 xmlParser.redirectEntity("http://www.w3.org/2001/XMLSchema.dtd", schemadtd);
375 xmlParser.redirectEntity("-//W3C//DTD XMLSCHEMA 200102//EN", schemadtd);
376
377 xmlParser.redirectEntity("jsp_2_0.xsd", jsp20xsd);
378 xmlParser.redirectEntity("http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd", jsp20xsd);
379 xmlParser.redirectEntity("j2ee_1_4.xsd", j2ee14xsd);
380 xmlParser.redirectEntity("http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd", j2ee14xsd);
381 xmlParser.redirectEntity("web-app_2_4.xsd",webapp24xsd);
382 xmlParser.redirectEntity("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd", webapp24xsd);
383 xmlParser.redirectEntity("xml.xsd", xmlxsd);
384 xmlParser.redirectEntity("http://www.w3.org/2001/xml.xsd", xmlxsd);
385 xmlParser.redirectEntity("datatypes.dtd", datatypesdtd);
386 xmlParser.redirectEntity("http://www.w3.org/2001/datatypes.dtd", datatypesdtd);
387 xmlParser.redirectEntity("j2ee_web_services_client_1_1.xsd", webservice11xsd);
388 xmlParser.redirectEntity("http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd", webservice11xsd);
389
390 // Find the webapp
391 resolveWebApp();
392
393 // Get the handler
394 getServletHandler();
395
396 // Add WEB-INF classes and lib classpaths
397 if (_webInf!=null && _webInf.isDirectory())
398 {
399 // Look for classes directory
400 Resource classes = _webInf.addPath("classes/");
401 if (classes.exists())
402 super.setClassPath(classes.toString());
403 else
404 super.setClassPath(null);
405
406 // Look for jars
407 Resource lib = _webInf.addPath("lib/");
408 super.setClassPaths(lib,true);
409 }
410
411 // initialize the classloader
412 initClassLoader(true);
413 thread.setContextClassLoader(getClassLoader());
414 initialize();
415
416 // Do the default configuration
417 if (_defaultsDescriptor!=null && _defaultsDescriptor.length()>0)
418 {
419 Resource dftResource= Resource.newSystemResource(_defaultsDescriptor);
420 if (dftResource==null)
421 dftResource= Resource.newResource(_defaultsDescriptor);
422
423 XmlParser.Node defaultConfig =
424 xmlParser.parse(dftResource.getURL().toString());
425 initialize(defaultConfig);
426 }
427
428 // handle any WEB-INF descriptors
429 if (_webInf!=null && _webInf.isDirectory())
430 {
431 // do web.xml file
432 Resource web = _webInf.addPath("web.xml");
433 if (!web.exists())
434 {
435 log.info("No WEB-INF/web.xml in "+_war+". Serving files and default/dynamic servlets only");
436 }
437 else
438 {
439 XmlParser.Node config=null;
440 _deploymentDescriptor=web.toString();
441 config = xmlParser.parse(web.getURL().toString());
442 initialize(config);
443 }
444
445 // do jetty.xml file
446 Resource jetty = _webInf.addPath("web-jetty.xml");
447 if (!jetty.exists())
448 jetty = _webInf.addPath("jetty-web.xml");
449 if (!_ignorewebjetty && jetty.exists())
450 {
451 if(log.isDebugEnabled())log.debug("Configure: "+jetty);
452 XmlConfiguration jetty_config=new
453 XmlConfiguration(jetty.getURL());
454 jetty_config.configure(this);
455 }
456 }
457
458 // Set classpath for Jasper.
459 Map.Entry entry = _webAppHandler.getHolderEntry("test.jsp");
460 if (entry!=null)
461 {
462 ServletHolder jspHolder = (ServletHolder)entry.getValue();
463 if (jspHolder!=null && jspHolder.getInitParameter("classpath")==null)
464 {
465 String fileClassPath=getFileClassPath();
466 jspHolder.setInitParameter("classpath",fileClassPath);
467 if(log.isDebugEnabled())log.debug("Set classpath="+fileClassPath+" for "+jspHolder);
468 }
469 }
470
471 // If we have servlets, don't init them yet
472 _webAppHandler.setAutoInitializeServlets(false);
473
474 // Start handlers
475 super.start();
476
477 mex = new MultiException();
478 // If it actually started
479 if (super.isStarted())
480 {
481 // Context listeners
482 if (_contextListeners!=null && _webAppHandler!=null)
483 {
484 ServletContextEvent event = new ServletContextEvent(getServletContext());
485 for (int i=0;i<LazyList.size(_contextListeners);i++)
486 try{((ServletContextListener)LazyList.get(_contextListeners,i))
487 .contextInitialized(event);}
488 catch(Exception ex) { mex.add(ex); }
489 }
490 }
491
492 // OK to Initialize servlets now
493 if (_webAppHandler!=null && _webAppHandler.isStarted())
494 {
495 try{
496 _webAppHandler.initializeServlets();
497 }
498 catch(Exception ex) { mex.add(ex); }
499 }
500 }
501 catch(Exception e)
502 {
503 log.warn("Configuration error on "+_war,e);
504 throw e;
505 }
506 finally
507 {
508 thread.setContextClassLoader(lastContextLoader);
509 }
510
511 if (mex!=null)
512 mex.ifExceptionThrow();
513 }
514
515
516 /* ------------------------------------------------------------ */
517 /** Stop the web application.
518 * Handlers for resource, servlet, filter and security are removed
519 * as they are recreated and configured by any subsequent call to start().
520 * @exception InterruptedException
521 */
522 public void stop()
523 throws InterruptedException
524 {
525 // Context listeners
526 if (_contextListeners!=null)
527 {
528 if (_webAppHandler!=null)
529 {
530 ServletContextEvent event = new ServletContextEvent(getServletContext());
531 for (int i=LazyList.size(_contextListeners);i-->0;)
532 ((ServletContextListener)LazyList.get(_contextListeners,i))
533 .contextDestroyed(event);
534 }
535 }
536
537 _contextListeners=null;
538 _contextAttributeListeners=null;
539
540 // Stop the context
541 super.stop();
542
543 // clean up
544 if (_webAppHandler!=null)
545 removeHandler(_webAppHandler);
546 _webAppHandler=null;
547
548 if (_errorPages!=null)
549 _errorPages.clear();
550 _errorPages=null;
551
552 }
553
554 /* ------------------------------------------------------------ */
555 public void handle(String pathInContext,
556 String pathParams,
557 HttpRequest httpRequest,
558 HttpResponse httpResponse)
559 throws HttpException, IOException
560 {
561 if (!isStarted())
562 return;
563 try
564 {
565 super.handle(pathInContext,pathParams,httpRequest,httpResponse);
566 }
567 finally
568 {
569 if (!httpRequest.isHandled())
570 httpResponse.sendError(HttpResponse.__404_Not_Found);
571 httpRequest.setHandled(true);
572 if (!httpResponse.isCommitted())
573 httpResponse.commit();
574 }
575 }
576
577
578 /* ------------------------------------------------------------ */
579 public synchronized void addEventListener(EventListener listener)
580 throws IllegalArgumentException
581 {
582 boolean known=false;
583 if (listener instanceof ServletContextListener)
584 {
585 known=true;
586 _contextListeners=LazyList.add(_contextListeners,listener);
587 }
588
589 if (listener instanceof ServletContextAttributeListener)
590 {
591 known=true;
592 _contextAttributeListeners=LazyList.add(_contextAttributeListeners,listener);
593 }
594
595
596 if (!known)
597 throw new IllegalArgumentException("Unknown "+listener);
598 }
599
600 /* ------------------------------------------------------------ */
601 public synchronized void removeEventListener(EventListener listener)
602 {
603 _contextListeners=LazyList.remove(_contextListeners,listener);
604 _contextAttributeListeners=LazyList.remove(_contextAttributeListeners,listener);
605 }
606
607 /* ------------------------------------------------------------ */
608 public synchronized void setAttribute(String name, Object value)
609 {
610 Object old = super.getAttribute(name);
611 super.setAttribute(name,value);
612
613 if (_contextAttributeListeners!=null && _webAppHandler!=null)
614 {
615 ServletContextAttributeEvent event =
616 new ServletContextAttributeEvent(getServletContext(),
617 name,
618 old!=null?old:value);
619 for (int i=0;i<LazyList.size(_contextAttributeListeners);i++)
620 {
621 ServletContextAttributeListener l =
622 (ServletContextAttributeListener)
623 LazyList.get(_contextAttributeListeners,i);
624 if (old==null)
625 l.attributeAdded(event);
626 else if (value==null)
627 l.attributeRemoved(event);
628 else
629 l.attributeReplaced(event);
630 }
631 }
632 }
633
634
635 /* ------------------------------------------------------------ */
636 public synchronized void removeAttribute(String name)
637 {
638 Object old = super.getAttribute(name);
639 super.removeAttribute(name);
640
641 if (old !=null &&
642 _contextAttributeListeners!=null &&
643 _webAppHandler!=null)
644 {
645 ServletContextAttributeEvent event =
646 new ServletContextAttributeEvent(getServletContext(),
647 name,old);
648 for (int i=0;i<LazyList.size(_contextAttributeListeners);i++)
649 {
650 ServletContextAttributeListener l =
651 (ServletContextAttributeListener)
652 LazyList.get(_contextAttributeListeners,i);
653 l.attributeRemoved(event);
654 }
655 }
656 }
657
658 /* ------------------------------------------------------------ */
659 public String getDisplayName()
660 {
661 return _name;
662 }
663
664 /* ------------------------------------------------------------ */
665 public String getDeploymentDescriptor()
666 {
667 return _deploymentDescriptor;
668 }
669
670
671 /* ------------------------------------------------------------ */
672 /** Set the defaults web.xml file.
673 * The default web.xml is used to configure all webapplications
674 * before the WEB-INF/web.xml file is applied. By default the
675 * org/mortbay/jetty/servlet/webdefault.xml resource from the
676 * org.mortbay.jetty.jar is used.
677 * @param defaults File, Resource, URL or null.
678 */
679 public void setDefaultsDescriptor(String defaults)
680 {
681 _defaultsDescriptor=defaults;
682 }
683
684 /* ------------------------------------------------------------ */
685 public String getDefaultsDescriptor()
686 {
687 return _defaultsDescriptor;
688 }
689
690 /* ------------------------------------------------------------ */
691 /**
692 * @param extract If true, a WAR is extracted to a temporary
693 * directory before being deployed.
694 */
695 public void setExtractWAR(boolean extract)
696 {
697 _extract=extract;
698 }
699
700 /* ------------------------------------------------------------ */
701 public boolean getExtractWAR()
702 {
703 return _extract;
704 }
705
706 /* ------------------------------------------------------------ */
707 /**
708 * Initialize is called by the start method after the contexts classloader
709 * has been initialied, but before the defaults descriptor has been applied.
710 * The default implementation does nothing.
711 *
712 * @exception Exception if an error occurs
713 */
714 protected void initialize()
715 throws Exception
716 {
717 }
718
719 /* ------------------------------------------------------------ */
720 protected void initialize(XmlParser.Node config)
721 throws ClassNotFoundException,UnavailableException
722 {
723 Iterator iter=config.iterator();
724 XmlParser.Node node=null;
725 while (iter.hasNext())
726 {
727 try
728 {
729 Object o = iter.next();
730 if (!(o instanceof XmlParser.Node))
731 continue;
732
733 node=(XmlParser.Node)o;
734 String name=node.getTag();
735
736 initWebXmlElement(name,node);
737 }
738 catch(ClassNotFoundException e)
739 {
740 throw e;
741 }
742 catch(Exception e)
743 {
744 log.warn("Configuration problem at "+node,e);
745 throw new UnavailableException("Configuration problem");
746 }
747 }
748
749 }
750
751 /* ------------------------------------------------------------ */
752 /** Handle web.xml element.
753 * This method is called for each top level element within the
754 * web.xml file. It may be specialized by derived
755 * WebApplicationContexts to provide additional configuration and handling.
756 * @param element The element name
757 * @param node The node containing the element.
758 */
759 protected void initWebXmlElement(String element, XmlParser.Node node)
760 throws Exception
761 {
762 if ("display-name".equals(element))
763 initDisplayName(node);
764 else if ("description".equals(element))
765 {}
766 else if ("context-param".equals(element))
767 initContextParam(node);
768 else if ("servlet".equals(element))
769 initServlet(node);
770 else if ("servlet-mapping".equals(element))
771 initServletMapping(node);
772 else if ("session-config".equals(element))
773 initSessionConfig(node);
774 else if ("mime-mapping".equals(element))
775 initMimeConfig(node);
776 else if ("welcome-file-list".equals(element))
777 initWelcomeFileList(node);
778 else if ("locale-encoding-mapping-list".equals(element))
779 initLocaleEncodingList(node);
780 else if ("error-page".equals(element))
781 initErrorPage(node);
782 else if ("taglib".equals(element))
783 initTagLib(node);
784 else if ("resource-ref".equals(element))
785 {
786 if(log.isDebugEnabled())log.debug("No implementation: "+node);
787 }
788 else if ("security-constraint".equals(element))
789 initSecurityConstraint(node);
790 else if ("login-config".equals(element))
791 initLoginConfig(node);
792 else if ("security-role".equals(element))
793 initSecurityRole(node);
794 else if ("filter".equals(element))
795 initFilter(node);
796 else if ("filter-mapping".equals(element))
797 initFilterMapping(node);
798 else if ("listener".equals(element))
799 initListener(node);
800 else
801 {
802 if (_warnings==null)
803 _warnings=new HashSet(3);
804
805 if (_warnings.contains(element))
806 {
807 if(log.isDebugEnabled())log.debug("Not Implemented: "+node);
808 }
809 else
810 {
811 _warnings.add(element);
812 if(log.isDebugEnabled())
813 {
814 log.debug("Element "+element+" not handled in "+this);
815 log.debug(node);
816 }
817 }
818 }
819 }
820
821 /* ------------------------------------------------------------ */
822 protected void initDisplayName(XmlParser.Node node)
823 {
824 _name=node.toString(false,true);
825 }
826
827 /* ------------------------------------------------------------ */
828 protected void initContextParam(XmlParser.Node node)
829 {
830 String name=node.getString("param-name",false,true);
831 String value=node.getString("param-value",false,true);
832 if(log.isDebugEnabled())log.debug("ContextParam: "+name+"="+value);
833
834 setInitParameter(name,value);
835 }
836
837 /* ------------------------------------------------------------ */
838 protected void initFilter(XmlParser.Node node)
839 throws ClassNotFoundException, UnavailableException
840 {
841 String name=node.getString("filter-name",false,true);
842 String className=node.getString("filter-class",false,true);
843
844 if (className==null)
845 {
846 log.warn("Missing filter-class in "+node);
847 return;
848 }
849 if (name==null)
850 name=className;
851
852 FilterHolder holder = _webAppHandler.defineFilter(name,className);
853 holder.addAppliesTo("REQUEST");
854 Iterator iter= node.iterator("init-param");
855 while(iter.hasNext())
856 {
857 XmlParser.Node paramNode=(XmlParser.Node)iter.next();
858 String pname=paramNode.getString("param-name",false,true);
859 String pvalue=paramNode.getString("param-value",false,true);
860 holder.put(pname,pvalue);
861 }
862 }
863
864 /* ------------------------------------------------------------ */
865 protected void initFilterMapping(XmlParser.Node node)
866 {
867 String filterName=node.getString("filter-name",false,true);
868 String pathSpec=node.getString("url-pattern",false,true);
869 String servletName=node.getString("servlet-name",false,true);
870
871
872 FilterHolder holder = (servletName!=null)
873 ?_webAppHandler.mapServletToFilter(servletName,filterName)
874 :_webAppHandler.mapPathToFilter(pathSpec,filterName);
875
876 Iterator iter= node.iterator("dispatcher");
877 while(iter.hasNext())
878 {
879 String dispatcher=((XmlParser.Node)iter.next())
880 .toString(false,true);
881 holder.addAppliesTo(dispatcher);
882 }
883 }
884
885 /* ------------------------------------------------------------ */
886 protected void initServlet(XmlParser.Node node)
887 throws ClassNotFoundException,
888 UnavailableException,
889 IOException,
890 MalformedURLException
891 {
892 String name=node.getString("servlet-name",false,true);
893 String className=node.getString("servlet-class",false,true);
894 String jspFile=null;
895
896 if (className==null)
897 {
898 // There is no class, so look for a jsp file
899 jspFile=node.getString("jsp-file",false,true);
900 if (jspFile!=null)
901 {
902 Map.Entry entry = _webAppHandler.getHolderEntry(jspFile);
903 if (entry!=null)
904 className=((ServletHolder)entry.getValue()).getClassName();
905 }
906
907 if (className==null)
908 {
909 log.warn("Missing servlet-class|jsp-file in "+node);
910 return;
911 }
912 }
913 if (name==null)
914 name=className;
915
916 ServletHolder holder = _webAppHandler.newServletHolder(name,className,jspFile);
917
918 // handle JSP classpath
919 if (jspFile!=null)
920 {
921 initClassLoader(true);
922 holder.setInitParameter("classpath",getFileClassPath());
923 }
924
925 Iterator iParamsIter= node.iterator("init-param");
926 while(iParamsIter.hasNext())
927 {
928 XmlParser.Node paramNode=(XmlParser.Node)iParamsIter.next();
929 String pname=paramNode.getString("param-name",false,true);
930 String pvalue=paramNode.getString("param-value",false,true);
931 holder.put(pname,pvalue);
932 }
933
934 XmlParser.Node startup = node.get("load-on-startup");
935 if (startup!=null)
936 {
937 String s=startup.toString(false,true).toLowerCase();
938 if (s.startsWith("t"))
939 {
940 log.warn("Deprecated boolean load-on-startup. Please use integer");
941 holder.setInitOrder(1);
942 }
943 else
944 {
945 int order=0;
946 try
947 {
948 if (s!=null && s.trim().length()>0)
949 order=Integer.parseInt(s);
950 }
951 catch(Exception e)
952 {
953 log.warn("Cannot parse load-on-startup "+s+". Please use integer");
954 LogSupport.ignore(log,e);
955 }
956 holder.setInitOrder(order);
957 }
958 }
959
960 Iterator sRefsIter= node.iterator("security-role-ref");
961 while(sRefsIter.hasNext())
962 {
963 XmlParser.Node securityRef=(XmlParser.Node)sRefsIter.next();
964 String roleName=securityRef.getString("role-name",false,true);
965 String roleLink=securityRef.getString("role-link",false,true);
966 if (roleName!=null && roleName.length()>0
967 && roleLink!=null && roleLink.length()>0)
968 {
969 if(log.isDebugEnabled())log.debug("link role "+roleName+" to "+roleLink+" for "+this);
970 holder.setUserRoleLink(roleName,roleLink);
971 }
972 else
973 {
974 log.warn("Ignored invalid security-role-ref element: "
975 +"servlet-name="+name+", "+securityRef);
976 }
977 }
978
979 XmlParser.Node run_as = node.get("run-as");
980 if (run_as!=null)
981 {
982 String roleName=run_as.getString("role-name",false,true);
983 if (roleName!=null)
984 holder.setRunAs(roleName);
985 }
986 }
987
988 /* ------------------------------------------------------------ */
989 protected void initServletMapping(XmlParser.Node node)
990 {
991 String name=node.getString("servlet-name",false,true);
992 String pathSpec=node.getString("url-pattern",false,true);
993
994 _webAppHandler.mapPathToServlet(pathSpec,name);
995 }
996
997 /* ------------------------------------------------------------ */
998 protected void initListener(XmlParser.Node node)
999 {
1000 String className=node.getString("listener-class",false,true);
1001 Object listener =null;
1002 try
1003 {
1004 Class listenerClass=loadClass(className);
1005 listener=listenerClass.newInstance();
1006 }
1007 catch(Exception e)
1008 {
1009 log.warn("Could not instantiate listener "+className,e);
1010 return;
1011 }
1012
1013 if (!(listener instanceof EventListener))
1014 {
1015 log.warn("Not an EventListener: "+listener);
1016 return;
1017 }
1018
1019 boolean known=false;
1020 try
1021 {
1022 addEventListener((EventListener)listener);
1023 known=true;
1024 }
1025 catch(Exception e)
1026 {
1027 LogSupport.ignore(log,e);
1028 }
1029
1030 try
1031 {
1032 _webAppHandler.addEventListener((EventListener)listener);
1033 known=true;
1034 }
1035 catch(Exception e)
1036 {
1037 LogSupport.ignore(log,e);
1038 }
1039
1040 if (!known)
1041 log.warn("Unknown: "+listener);
1042 }
1043
1044
1045 /* ------------------------------------------------------------ */
1046 protected void initSessionConfig(XmlParser.Node node)
1047 {
1048 XmlParser.Node tNode=node.get("session-timeout");
1049 if(tNode!=null)
1050 {
1051 int timeout = Integer.parseInt(tNode.toString(false,true));
1052 _webAppHandler.setSessionInactiveInterval(timeout*60);
1053 }
1054 }
1055
1056 /* ------------------------------------------------------------ */
1057 protected void initMimeConfig(XmlParser.Node node)
1058 {
1059 String extension= node.getString("extension",false,true);
1060 if (extension!=null && extension.startsWith("."))
1061 extension=extension.substring(1);
1062
1063 String mimeType= node.getString("mime-type",false,true);
1064 setMimeMapping(extension,mimeType);
1065 }
1066
1067 /* ------------------------------------------------------------ */
1068 protected void initWelcomeFileList(XmlParser.Node node)
1069 {
1070 Iterator iter= node.iterator("welcome-file");
1071 while(iter.hasNext())
1072 {
1073 XmlParser.Node indexNode=(XmlParser.Node)iter.next();
1074 String index=indexNode.toString(false,true);
1075 if(log.isDebugEnabled())log.debug("Index: "+index);
1076 addWelcomeFile(index);
1077 }
1078 }
1079
1080 /* ------------------------------------------------------------ */
1081 protected void initLocaleEncodingList(XmlParser.Node node)
1082 {
1083 Iterator iter= node.iterator("locale-encoding-mapping");
1084 while(iter.hasNext())
1085 {
1086 XmlParser.Node mapping=(XmlParser.Node)iter.next();
1087 String locale= mapping.getString("locale",false,true);
1088 String encoding= mapping.getString("encoding",false,true);
1089 addLocaleEncoding(locale,encoding);
1090 }
1091 }
1092
1093 /* ------------------------------------------------------------ */
1094 protected void initErrorPage(XmlParser.Node node)
1095 {
1096 String error= node.getString("error-code",false,true);
1097 if (error==null || error.length()==0)
1098 error= node.getString("exception-type",false,true);
1099
1100 String location= node.getString("location",false,true);
1101 setErrorPage(error,location);
1102 }
1103
1104 /* ------------------------------------------------------------ */
1105 protected void initTagLib(XmlParser.Node node)
1106 {
1107 String uri= node.getString("taglib-uri",false,true);
1108 String location= node.getString("taglib-location",false,true);
1109 _tagLibMap.put(uri,location);
1110 setResourceAlias(uri,location);
1111 }
1112
1113 /* ------------------------------------------------------------ */
1114 protected void initSecurityConstraint(XmlParser.Node node)
1115 {
1116 SecurityConstraint scBase = new SecurityConstraint();
1117
1118 XmlParser.Node auths=node.get("auth-constraint");
1119 if (auths!=null)
1120 {
1121 scBase.setAuthenticate(true);
1122 // auth-constraint
1123 Iterator iter= auths.iterator("role-name");
1124 while(iter.hasNext())
1125 {
1126 String role=((XmlParser.Node)iter.next()).toString(false,true);
1127 scBase.addRole(role);
1128 }
1129 }
1130
1131 XmlParser.Node data=node.get("user-data-constraint");
1132 if (data!=null)
1133 {
1134 data=data.get("transport-guarantee");
1135 String guarantee = data.toString(false,true).toUpperCase();
1136 if (guarantee==null || guarantee.length()==0 ||
1137 "NONE".equals(guarantee))
1138 scBase.setDataConstraint(SecurityConstraint.DC_NONE);
1139 else if ("INTEGRAL".equals(guarantee))
1140 scBase.setDataConstraint(SecurityConstraint.DC_INTEGRAL);
1141 else if ("CONFIDENTIAL".equals(guarantee))
1142 scBase.setDataConstraint(SecurityConstraint.DC_CONFIDENTIAL);
1143 else
1144 {
1145 log.warn("Unknown user-data-constraint:"+guarantee);
1146 scBase.setDataConstraint(SecurityConstraint.DC_CONFIDENTIAL);
1147 }
1148 }
1149
1150 Iterator iter= node.iterator("web-resource-collection");
1151 while(iter.hasNext())
1152 {
1153 XmlParser.Node collection=(XmlParser.Node)iter.next();
1154 String name=collection.getString("web-resource-name",false,true);
1155 SecurityConstraint sc = (SecurityConstraint)scBase.clone();
1156 sc.setName(name);
1157
1158 Iterator iter2= collection.iterator("http-method");
1159 while(iter2.hasNext())
1160 sc.addMethod(((XmlParser.Node)iter2.next())
1161 .toString(false,true));
1162
1163 iter2= collection.iterator("url-pattern");
1164 while(iter2.hasNext())
1165 {
1166 String url=
1167 ((XmlParser.Node)iter2.next()).toString(false,true);
1168 addSecurityConstraint(url,sc);
1169 }
1170 }
1171 }
1172
1173 /* ------------------------------------------------------------ */
1174 protected void initLoginConfig(XmlParser.Node node)
1175 {
1176 XmlParser.Node method=node.get("auth-method");
1177 if (method!=null)
1178 {
1179 Authenticator authenticator=null;
1180 String m=method.toString(false,true);
1181
1182 if (SecurityConstraint.__FORM_AUTH.equals(m))
1183 authenticator=_formAuthenticator=new FormAuthenticator();
1184 else if (SecurityConstraint.__BASIC_AUTH.equals(m))
1185 authenticator=new BasicAuthenticator();
1186 else if (SecurityConstraint.__DIGEST_AUTH.equals(m))
1187 authenticator=new DigestAuthenticator();
1188 else if (SecurityConstraint.__CERT_AUTH.equals(m))
1189 authenticator=new ClientCertAuthenticator();
1190 else if (SecurityConstraint.__CERT_AUTH2.equals(m))
1191 authenticator=new ClientCertAuthenticator();
1192 else
1193 log.warn("UNKNOWN AUTH METHOD: "+m);
1194
1195 setAuthenticator(authenticator);
1196 }
1197
1198 XmlParser.Node name=node.get("realm-name");
1199 if (name!=null)
1200 setRealmName(name.toString(false,true));
1201
1202 XmlParser.Node formConfig = node.get("form-login-config");
1203 if(formConfig != null)
1204 {
1205 if (_formAuthenticator==null)
1206 log.warn("FORM Authentication miss-configured");
1207 else
1208 {
1209 XmlParser.Node loginPage = formConfig.get("form-login-page");
1210 if (loginPage != null)
1211 _formAuthenticator.setLoginPage(loginPage.toString(false,true));
1212 XmlParser.Node errorPage = formConfig.get("form-error-page");
1213 if (errorPage != null)
1214 {
1215 String ep=errorPage.toString(false,true);
1216 _formAuthenticator.setErrorPage(ep);
1217 }
1218 }
1219 }
1220 }
1221
1222 /* ------------------------------------------------------------ */
1223 protected void initSecurityRole(XmlParser.Node node)
1224 {
1225 }
1226
1227 /* ------------------------------------------------------------ */
1228 protected UserRealm getUserRealm(String name)
1229 {
1230 return getHttpServer().getRealm(name);
1231 }
1232
1233 /* ------------------------------------------------------------ */
1234 public String toString()
1235 {
1236 return "WebApplicationContext["+getHttpContextName()+","+
1237 (_name==null?_war:_name)+"]";
1238 }
1239
1240 /* ------------------------------------------------------------ */
1241 public void setClassPath(String classPath)
1242 {
1243 log.warn("ClassPath should not be set for WebApplication");
1244 super.setClassPath(classPath);
1245 }
1246
1247 /* ------------------------------------------------------------ */
1248 public void setResourceBase(String resourceBase)
1249 {
1250 log.warn("ResourceBase should not be set for WebApplication");
1251 super.setResourceBase(resourceBase);
1252 }
1253
1254 /* ------------------------------------------------------------ */
1255 public void setBaseResource(Resource baseResource)
1256 {
1257 log.warn("BaseResource should not be set for WebApplication");
1258 super.setBaseResource(baseResource);
1259 }
1260
1261 /* ------------------------------------------------------------ */
1262 /** Get the taglib map.
1263 * @return A map of uri to location for tag libraries.
1264 */
1265 public Map getTagLibMap()
1266 {
1267 return _tagLibMap;
1268 }
1269
1270
1271 /* ------------------------------------------------------------ */
1272 /** Set Resource Alias.
1273 * Resource aliases map resource uri's within a context.
1274 * They may optionally be used by a handler when looking for
1275 * a resource.
1276 * @param alias
1277 * @param uri
1278 */
1279 public void setResourceAlias(String alias,String uri)
1280 {
1281 if (_resourceAliases==null)
1282 _resourceAliases=new HashMap(5);
1283 _resourceAliases.put(alias,uri);
1284 }
1285
1286 /* ------------------------------------------------------------ */
1287 public String getResourceAlias(String alias)
1288 {
1289 if (_resourceAliases==null)
1290 return null;
1291 return (String) _resourceAliases.get(alias);
1292 }
1293
1294 /* ------------------------------------------------------------ */
1295 public String removeResourceAlias(String alias)
1296 {
1297 if (_resourceAliases==null)
1298 return null;
1299 return (String) _resourceAliases.remove(alias);
1300 }
1301
1302
1303 /* ------------------------------------------------------------ */
1304 public Resource getResource(String uriInContext)
1305 throws IOException
1306 {
1307 IOException ioe=null;
1308 Resource resource=null;
1309 try
1310 {
1311 resource=super.getResource(uriInContext);
1312 if (resource!=null && resource.exists())
1313 return resource;
1314 }
1315 catch (IOException e)
1316 {
1317 ioe=e;
1318 }
1319
1320 String aliasedUri=getResourceAlias(uriInContext);
1321 if (aliasedUri!=null)
1322 return super.getResource(aliasedUri);
1323
1324 if (ioe!=null)
1325 throw ioe;
1326
1327 return resource;
1328 }
1329
1330
1331 /* ------------------------------------------------------------ */
1332 /** set error page URI.
1333 * @param error A string representing an error code or a
1334 * exception classname
1335 * @param uriInContext
1336 */
1337 void setErrorPage(String error,String uriInContext)
1338 {
1339 if (_errorPages==null)
1340 _errorPages=new HashMap();
1341 _errorPages.put(error,uriInContext);
1342 }
1343
1344 /* ------------------------------------------------------------ */
1345 /** get error page URI.
1346 * @param error A string representing an error code or a
1347 * exception classname
1348 * @return URI within context
1349 */
1350 String getErrorPage(String error)
1351 {
1352 if (_errorPages==null)
1353 return null;
1354 return (String) _errorPages.get(error);
1355 }
1356
1357
1358 /* ------------------------------------------------------------ */
1359 String removeErrorPage(String error)
1360 {
1361 if (_errorPages==null)
1362 return null;
1363 return (String) _errorPages.remove(error);
1364 }
1365
1366}