View Javadoc

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.quickstart;
20  
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.EventListener;
28  import java.util.HashMap;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import javax.servlet.DispatcherType;
33  import javax.servlet.MultipartConfigElement;
34  import javax.servlet.ServletContext;
35  import javax.servlet.SessionCookieConfig;
36  import javax.servlet.SessionTrackingMode;
37  import javax.servlet.descriptor.JspPropertyGroupDescriptor;
38  import javax.servlet.descriptor.TaglibDescriptor;
39  
40  import org.eclipse.jetty.annotations.AnnotationConfiguration;
41  import org.eclipse.jetty.http.MimeTypes;
42  import org.eclipse.jetty.plus.annotation.LifeCycleCallback;
43  import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
44  import org.eclipse.jetty.security.ConstraintAware;
45  import org.eclipse.jetty.security.ConstraintMapping;
46  import org.eclipse.jetty.security.SecurityHandler;
47  import org.eclipse.jetty.security.authentication.FormAuthenticator;
48  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
49  import org.eclipse.jetty.servlet.FilterHolder;
50  import org.eclipse.jetty.servlet.FilterMapping;
51  import org.eclipse.jetty.servlet.Holder;
52  import org.eclipse.jetty.servlet.ServletHandler;
53  import org.eclipse.jetty.servlet.ServletHolder;
54  import org.eclipse.jetty.servlet.ServletMapping;
55  import org.eclipse.jetty.util.QuotedStringTokenizer;
56  import org.eclipse.jetty.util.log.Log;
57  import org.eclipse.jetty.util.log.Logger;
58  import org.eclipse.jetty.util.resource.JarResource;
59  import org.eclipse.jetty.util.resource.Resource;
60  import org.eclipse.jetty.util.security.Constraint;
61  import org.eclipse.jetty.webapp.MetaData;
62  import org.eclipse.jetty.webapp.MetaData.OriginInfo;
63  import org.eclipse.jetty.webapp.MetaInfConfiguration;
64  import org.eclipse.jetty.webapp.WebAppContext;
65  import org.eclipse.jetty.xml.XmlAppendable;
66  
67  /**
68   * QuickStartWar
69   *
70   */
71  public class QuickStartWebApp extends WebAppContext
72  {
73      private static final Logger LOG = Log.getLogger(QuickStartWebApp.class);
74      
75      public static final String[] __configurationClasses = new String[] 
76              {
77                  org.eclipse.jetty.quickstart.QuickStartConfiguration.class.getCanonicalName(),
78                  org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(),
79                  org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
80                  org.eclipse.jetty.webapp.JettyWebXmlConfiguration.class.getCanonicalName()
81              };
82      
83      
84      private boolean _preconfigure=false;
85      private boolean _autoPreconfigure=false;
86      private boolean _startWebapp=false;
87      private PreconfigureDescriptorProcessor _preconfigProcessor;
88      
89  
90      public static final String[] __preconfigurationClasses = new String[]
91      { 
92          org.eclipse.jetty.webapp.WebInfConfiguration.class.getCanonicalName(), 
93          org.eclipse.jetty.webapp.WebXmlConfiguration.class.getCanonicalName(),
94          org.eclipse.jetty.webapp.MetaInfConfiguration.class.getCanonicalName(), 
95          org.eclipse.jetty.webapp.FragmentConfiguration.class.getCanonicalName(),
96          org.eclipse.jetty.plus.webapp.EnvConfiguration.class.getCanonicalName(), 
97          org.eclipse.jetty.plus.webapp.PlusConfiguration.class.getCanonicalName(),
98          org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName(),
99      };
100     
101     public QuickStartWebApp()
102     {
103         super();
104         setConfigurationClasses(__preconfigurationClasses);
105         setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*\\.jar");
106     }
107 
108     public boolean isPreconfigure()
109     {
110         return _preconfigure;
111     }
112 
113     /* ------------------------------------------------------------ */
114     /** Preconfigure webapp
115      * @param preconfigure  If true, then starting the webapp will generate 
116      * the WEB-INF/quickstart-web.xml rather than start the webapp.
117      */
118     public void setPreconfigure(boolean preconfigure)
119     {
120         _preconfigure = preconfigure;
121     }
122 
123     public boolean isAutoPreconfigure()
124     {
125         return _autoPreconfigure;
126     }
127     
128     public void setAutoPreconfigure(boolean autoPrecompile)
129     {
130         _autoPreconfigure = autoPrecompile;
131     }
132     
133     @Override
134     protected void startWebapp() throws Exception
135     {
136         if (isPreconfigure())
137             generateQuickstartWebXml(_preconfigProcessor.getXML());
138         
139         if (_startWebapp)
140             super.startWebapp();
141     }
142     
143     @Override
144     protected void doStart() throws Exception
145     {
146         // unpack and Adjust paths.
147         Resource war = null;
148         Resource dir = null;
149 
150         Resource base = getBaseResource();
151         if (base==null)
152             base=Resource.newResource(getWar());
153 
154         if (base.isDirectory())
155             dir=base;
156         else if (base.toString().toLowerCase().endsWith(".war"))
157         {
158             war=base;
159             String w=war.toString();
160             dir=Resource.newResource(w.substring(0,w.length()-4));
161 
162             if (!dir.exists())
163             {                       
164                 LOG.info("Quickstart Extract " + war + " to " + dir);
165                 dir.getFile().mkdirs();
166                 JarResource.newJarResource(war).copyTo(dir.getFile());
167             }
168 
169             setWar(null);
170             setBaseResource(dir);
171         }
172         else 
173             throw new IllegalArgumentException();
174 
175 
176         Resource qswebxml=dir.addPath("/WEB-INF/quickstart-web.xml");
177         
178         if (isPreconfigure())
179         {
180             _preconfigProcessor = new PreconfigureDescriptorProcessor();
181             getMetaData().addDescriptorProcessor(_preconfigProcessor);
182             _startWebapp=false;
183         }
184         else if (qswebxml.exists())
185         {
186             setConfigurationClasses(__configurationClasses);
187             _startWebapp=true;
188         }
189         else if (_autoPreconfigure)
190         {   
191             LOG.info("Quickstart preconfigure: {}(war={},dir={})",this,war,dir);
192 
193             _preconfigProcessor = new PreconfigureDescriptorProcessor();
194             
195             getMetaData().addDescriptorProcessor(_preconfigProcessor);
196             setPreconfigure(true);
197             _startWebapp=true;
198         }
199         else
200             _startWebapp=true;
201             
202         super.doStart();
203     }
204 
205 
206     public void generateQuickstartWebXml(String extraXML) throws IOException
207     {
208         getMetaData().getOrigins();
209         // dumpStdErr();
210 
211         if (getBaseResource()==null)
212             throw new IllegalArgumentException("No base resource for "+this);
213         
214         File webxml = new File(getWebInf().getFile(),"quickstart-web.xml");
215 
216         LOG.info("Quickstart generate {}",webxml);
217 
218         XmlAppendable out = new XmlAppendable(new FileOutputStream(webxml),"UTF-8");
219         MetaData md = getMetaData();
220 
221         Map<String, String> webappAttr = new HashMap<>();
222         webappAttr.put("xmlns","http://xmlns.jcp.org/xml/ns/javaee");
223         webappAttr.put("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
224         webappAttr.put("xsi:schemaLocation","http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd");
225         webappAttr.put("metadata-complete","true");
226         webappAttr.put("version","3.1");
227 
228         out.open("web-app",webappAttr);
229 
230         if (getDisplayName() != null)
231             out.tag("display-name",getDisplayName());
232 
233         // Set some special context parameters
234 
235         // The location of the war file on disk
236         String resourceBase=getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString();
237         
238         // The library order
239         addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS);
240         //the servlet container initializers
241         addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS);
242         //the tlds discovered
243         addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase);
244         //the META-INF/resources discovered
245         addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase);
246 
247         
248         // init params
249         for (String p : getInitParams().keySet())
250             out.open("context-param",origin(md,"context-param." + p))
251             .tag("param-name",p)
252             .tag("param-value",getInitParameter(p))
253             .close();
254 
255         if (getEventListeners() != null)
256             for (EventListener e : getEventListeners())
257                 out.open("listener",origin(md,e.getClass().getCanonicalName() + ".listener"))
258                 .tag("listener-class",e.getClass().getCanonicalName())
259                 .close();
260 
261         ServletHandler servlets = getServletHandler();
262 
263         if (servlets.getFilters() != null)
264         {
265             for (FilterHolder holder : servlets.getFilters())
266                 outholder(out,md,"filter",holder);
267         }
268 
269         if (servlets.getFilterMappings() != null)
270         {
271             for (FilterMapping mapping : servlets.getFilterMappings())
272             {
273                 out.open("filter-mapping");
274                 out.tag("filter-name",mapping.getFilterName());
275                 if (mapping.getPathSpecs() != null)
276                     for (String s : mapping.getPathSpecs())
277                         out.tag("url-pattern",s);
278                 if (mapping.getServletNames() != null)
279                     for (String n : mapping.getServletNames())
280                         out.tag("servlet-name",n);
281 
282                 if (!mapping.isDefaultDispatches())
283                 {
284                     if (mapping.appliesTo(DispatcherType.REQUEST))
285                         out.tag("dispatcher","REQUEST");
286                     if (mapping.appliesTo(DispatcherType.ASYNC))
287                         out.tag("dispatcher","ASYNC");
288                     if (mapping.appliesTo(DispatcherType.ERROR))
289                         out.tag("dispatcher","ERROR");
290                     if (mapping.appliesTo(DispatcherType.FORWARD))
291                         out.tag("dispatcher","FORWARD");
292                     if (mapping.appliesTo(DispatcherType.INCLUDE))
293                         out.tag("dispatcher","INCLUDE");
294                 }
295                 out.close();
296             }
297         }
298 
299         if (servlets.getServlets() != null)
300         {
301             for (ServletHolder holder : servlets.getServlets())
302                 outholder(out,md,"servlet",holder);
303         }
304 
305         if (servlets.getServletMappings() != null)
306         {
307             for (ServletMapping mapping : servlets.getServletMappings())
308             {
309                 out.open("servlet-mapping",origin(md,mapping.getServletName() + ".servlet.mappings"));
310                 out.tag("servlet-name",mapping.getServletName());
311                 if (mapping.getPathSpecs() != null)
312                     for (String s : mapping.getPathSpecs())
313                         out.tag("url-pattern",s);
314                 out.close();
315             }
316         }
317 
318         // Security elements
319         SecurityHandler security = getSecurityHandler();
320         
321         if (security!=null && (security.getRealmName()!=null || security.getAuthMethod()!=null))
322         {
323             out.open("login-config");
324             if (security.getAuthMethod()!=null)
325                 out.tag("auth-method",origin(md,"auth-method"),security.getAuthMethod());
326             if (security.getRealmName()!=null)
327                 out.tag("realm-name",origin(md,"realm-name"),security.getRealmName());
328             
329             
330             if (Constraint.__FORM_AUTH.equalsIgnoreCase(security.getAuthMethod()))
331             {
332                 out.open("form-login-config");
333                 out.tag("form-login-page",origin(md,"form-login-page"),security.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE));
334                 out.tag("form-error-page",origin(md,"form-error-page"),security.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE));
335                 out.close();
336             }
337             
338             out.close();
339         }
340         
341         if (security instanceof ConstraintAware)
342         {
343             ConstraintAware ca = (ConstraintAware)security;
344             for (String r:ca.getRoles())
345                 out.open("security-role")
346                 .tag("role-name",r)
347                 .close();
348             
349             for (ConstraintMapping m : ca.getConstraintMappings())
350             {
351                 out.open("security-constraint");
352                 
353                 if (m.getConstraint().getAuthenticate())
354                 {
355                     out.open("auth-constraint");
356                     if (m.getConstraint().getRoles()!=null)
357                         for (String r : m.getConstraint().getRoles())
358                             out.tag("role-name",r);
359 
360                     out.close();
361                 }
362                 
363                 switch (m.getConstraint().getDataConstraint())
364                 {
365                     case Constraint.DC_NONE:
366                         out.open("user-data-constraint").tag("transport-guarantee","NONE").close();
367                         break;
368                         
369                     case Constraint.DC_INTEGRAL:
370                         out.open("user-data-constraint").tag("transport-guarantee","INTEGRAL").close();
371                         break;
372                         
373                     case Constraint.DC_CONFIDENTIAL:
374                         out.open("user-data-constraint").tag("transport-guarantee","CONFIDENTIAL").close();
375                         break;
376                         
377                     default:
378                             break;
379                         
380                 }
381 
382                 out.open("web-resource-collection");
383                 {
384                     if (m.getConstraint().getName()!=null)
385                         out.tag("web-resource-name",m.getConstraint().getName());
386                     if (m.getPathSpec()!=null)
387                         out.tag("url-pattern",origin(md,"constraint.url."+m.getPathSpec()),m.getPathSpec());
388                     if (m.getMethod()!=null)
389                         out.tag("http-method",m.getMethod());
390 
391                     if (m.getMethodOmissions()!=null)
392                         for (String o:m.getMethodOmissions())
393                             out.tag("http-method-omission",o);
394 
395                     out.close();
396                 }
397                 
398                 out.close();
399                 
400             }
401         }
402         
403         if (getWelcomeFiles() != null)
404         {
405             out.open("welcome-file-list");
406             for (String welcomeFile:getWelcomeFiles())
407             {
408                 out.tag("welcome-file", welcomeFile);
409             }
410             out.close();
411         }
412     
413         Map<String,String> localeEncodings = getLocaleEncodings();
414         if (localeEncodings != null && !localeEncodings.isEmpty())
415         {
416             out.open("locale-encoding-mapping-list");
417             for (Map.Entry<String, String> entry:localeEncodings.entrySet())
418             {
419                 out.open("locale-encoding-mapping", origin(md,"locale-encoding."+entry.getKey()));
420                 out.tag("locale", entry.getKey());
421                 out.tag("encoding", entry.getValue());
422                 out.close();
423             }
424             out.close();
425         }
426 
427         //session-config
428         if (getSessionHandler().getSessionManager() != null)
429         {
430             out.open("session-config");
431             int maxInactiveSec = getSessionHandler().getSessionManager().getMaxInactiveInterval();
432             out.tag("session-timeout", (maxInactiveSec==0?"0":Integer.toString(maxInactiveSec/60)));
433  
434             Set<SessionTrackingMode> modes = getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes();
435             if (modes != null)
436             {
437                 for (SessionTrackingMode mode:modes)
438                     out.tag("tracking-mode", mode.toString());
439             }
440             
441             //cookie-config
442             SessionCookieConfig cookieConfig = getSessionHandler().getSessionManager().getSessionCookieConfig();
443             if (cookieConfig != null)
444             {
445                 out.open("cookie-config");
446                 if (cookieConfig.getName() != null)
447                     out.tag("name", origin(md,"cookie-config.name"), cookieConfig.getName());
448                 
449                 if (cookieConfig.getDomain() != null)
450                     out.tag("domain", origin(md, "cookie-config.domain"), cookieConfig.getDomain());
451                 
452                 if (cookieConfig.getPath() != null)
453                     out.tag("path", origin(md, "cookie-config.path"), cookieConfig.getPath());
454                 
455                 if (cookieConfig.getComment() != null)
456                     out.tag("comment", origin(md, "cookie-config.comment"), cookieConfig.getComment());
457                 
458                 out.tag("http-only", origin(md, "cookie-config.http-only"), Boolean.toString(cookieConfig.isHttpOnly()));
459                 out.tag("secure", origin(md, "cookie-config.secure"), Boolean.toString(cookieConfig.isSecure()));
460                 out.tag("max-age", origin(md, "cookie-config.max-age"), Integer.toString(cookieConfig.getMaxAge()));
461                 out.close();
462             }
463             out.close();     
464         }
465         
466         //error-pages
467         Map<String,String> errorPages = ((ErrorPageErrorHandler)getErrorHandler()).getErrorPages();
468         if (errorPages != null)
469         {
470             for (Map.Entry<String, String> entry:errorPages.entrySet())
471             {
472                 out.open("error-page", origin(md, "error."+entry.getKey()));
473                 //a global or default error page has no code or exception               
474                 if (!ErrorPageErrorHandler.GLOBAL_ERROR_PAGE.equals(entry.getKey()))
475                 {
476                     if (entry.getKey().matches("\\d{3}"))
477                         out.tag("error-code", entry.getKey());
478                     else
479                         out.tag("exception-type", entry.getKey());
480                 }
481                 out.tag("location", entry.getValue());
482                 out.close();
483             }
484         }
485         
486         //mime-types
487         MimeTypes mimeTypes = getMimeTypes();
488         if (mimeTypes != null)
489         {
490             for (Map.Entry<String, String> entry:mimeTypes.getMimeMap().entrySet())
491             {
492                 out.open("mime-mapping");
493                 out.tag("extension", origin(md, "extension."+entry.getKey()), entry.getKey());
494                 out.tag("mime-type", entry.getValue());
495                 out.close();
496             }
497         }
498         
499         //jsp-config
500         JspConfig jspConfig = (JspConfig)getServletContext().getJspConfigDescriptor();
501         if (jspConfig != null)
502         {
503             out.open("jsp-config");
504             Collection<TaglibDescriptor> tlds = jspConfig.getTaglibs();
505             if (tlds != null && !tlds.isEmpty())
506             {
507                 for (TaglibDescriptor tld:tlds)
508                 {
509                     out.open("taglib");
510                     out.tag("taglib-uri", tld.getTaglibURI());
511                     out.tag("taglib-location", tld.getTaglibLocation());
512                     out.close();
513                 }
514             }
515             
516             Collection<JspPropertyGroupDescriptor> jspPropertyGroups = jspConfig.getJspPropertyGroups();
517             if (jspPropertyGroups != null && !jspPropertyGroups.isEmpty())
518             {
519                 for (JspPropertyGroupDescriptor jspPropertyGroup:jspPropertyGroups)
520                 {
521                     out.open("jsp-property-group");
522                     Collection<String> strings = jspPropertyGroup.getUrlPatterns();
523                     if (strings != null && !strings.isEmpty())
524                     {
525                         for (String urlPattern:strings)
526                             out.tag("url-pattern", urlPattern);
527                     }
528 
529                     if (jspPropertyGroup.getElIgnored() != null)
530                         out.tag("el-ignored", jspPropertyGroup.getElIgnored());
531 
532                     if (jspPropertyGroup.getPageEncoding() != null)
533                         out.tag("page-encoding", jspPropertyGroup.getPageEncoding());
534 
535                     if (jspPropertyGroup.getScriptingInvalid() != null)
536                         out.tag("scripting-invalid", jspPropertyGroup.getScriptingInvalid());
537 
538                     if (jspPropertyGroup.getIsXml() != null)
539                         out.tag("is-xml", jspPropertyGroup.getIsXml());
540 
541                     if (jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral() != null)
542                         out.tag("deferred-syntax-allowed-as-literal", jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral());
543 
544                     if (jspPropertyGroup.getTrimDirectiveWhitespaces() != null)
545                         out.tag("trim-directive-whitespaces", jspPropertyGroup.getTrimDirectiveWhitespaces());
546 
547                     if (jspPropertyGroup.getDefaultContentType() != null)
548                         out.tag("default-content-type", jspPropertyGroup.getDefaultContentType());
549 
550                     if (jspPropertyGroup.getBuffer() != null)
551                         out.tag("buffer", jspPropertyGroup.getBuffer());
552 
553                     if (jspPropertyGroup.getErrorOnUndeclaredNamespace() != null)
554                         out.tag("error-on-undeclared-namespace", jspPropertyGroup.getErrorOnUndeclaredNamespace());
555 
556                     strings = jspPropertyGroup.getIncludePreludes();
557                     if (strings != null && !strings.isEmpty())
558                     {
559                         for (String prelude:strings)
560                             out.tag("include-prelude", prelude);
561                     }
562                    
563                     strings = jspPropertyGroup.getIncludeCodas();
564                     if (strings != null && !strings.isEmpty())
565                     {
566                         for (String coda:strings)
567                             out.tag("include-coda", coda);
568                     }
569                    
570                     out.close();
571                 }
572             }
573             
574             out.close();
575         }
576 
577         //lifecycle: post-construct, pre-destroy
578         LifeCycleCallbackCollection lifecycles = ((LifeCycleCallbackCollection)getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION));
579         if (lifecycles != null)
580         {
581             Collection<LifeCycleCallback> tmp = lifecycles.getPostConstructCallbacks();
582 
583             for (LifeCycleCallback c:tmp)
584             {
585                 out.open("post-construct");
586                 out.tag("lifecycle-callback-class", c.getTargetClassName());
587                 out.tag("lifecycle-callback-method", c.getMethodName());
588                 out.close();
589             }
590             
591             tmp = lifecycles.getPreDestroyCallbacks();
592             for (LifeCycleCallback c:tmp)
593             {
594                 out.open("pre-destroy");
595                 out.tag("lifecycle-callback-class", c.getTargetClassName());
596                 out.tag("lifecycle-callback-method", c.getMethodName());
597                 out.close();
598             }
599         }
600 
601         out.literal(extraXML);
602         
603         out.close();
604     }
605 
606     private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException
607     {
608         addContextParamFromAttribute(out,attribute,null);
609     }
610     
611     private void addContextParamFromAttribute(XmlAppendable out, String attribute, String resourceBase) throws IOException
612     {
613         Object o=getAttribute(attribute);
614         if (o==null)
615             return;
616                 
617         Collection<?> c =  (o instanceof Collection)? (Collection<?>)o:Collections.singletonList(o);
618         StringBuilder v=new StringBuilder();
619         for (Object i:c)
620         {
621             if (i!=null)
622             {
623                 if (v.length()>0)
624                     v.append(",\n    ");
625                 else
626                     v.append("\n    ");
627                 if (resourceBase==null)
628                     QuotedStringTokenizer.quote(v,i.toString());
629                 else
630                     QuotedStringTokenizer.quote(v,i.toString().replace(resourceBase,"${WAR}/"));
631             }
632         }
633         out.open("context-param")
634         .tag("param-name",attribute)
635         .tagCDATA("param-value",v.toString())
636         .close();        
637     }
638 
639     private static void outholder(XmlAppendable out, MetaData md, String tag, Holder<?> holder) throws IOException
640     {
641         out.open(tag,Collections.singletonMap("source",holder.getSource().toString()));
642         String n = holder.getName();
643         out.tag(tag + "-name",n);
644 
645         String ot = n + "." + tag + ".";
646 
647         out.tag(tag + "-class",origin(md,ot + tag + "-class"),holder.getClassName());
648 
649         for (String p : holder.getInitParameters().keySet())
650         {
651             if ("scratchdir".equalsIgnoreCase(p)) //don't preconfigure the temp dir for jsp output
652                 continue;
653             out.open("init-param",origin(md,ot + "init-param." + p))
654             .tag("param-name",p)
655             .tag("param-value",holder.getInitParameter(p))
656             .close();
657         }
658 
659         if (holder instanceof ServletHolder)
660         {
661             ServletHolder s = (ServletHolder)holder;
662             if (s.getForcedPath() != null)
663                 out.tag("jsp-file",s.getForcedPath());
664 
665             if (s.getInitOrder() != 0)
666                 out.tag("load-on-startup",Integer.toString(s.getInitOrder()));
667 
668             if (s.getRunAsRole() != null)
669                 out.open("run-as",origin(md,ot + "run-as"))
670                 .tag("role-name",s.getRunAsRole())
671                 .close();
672 
673             Map<String,String> roles = s.getRoleRefMap();
674             if (roles!=null)
675             {
676                 for (Map.Entry<String, String> e : roles.entrySet())
677                 {
678                     out.open("security-role-ref",origin(md,ot+"role-name."+e.getKey()))
679                     .tag("role-name",e.getKey())
680                     .tag("role-link",e.getValue())
681                     .close();
682                 }
683             }
684             
685             if (!s.isEnabled())
686                 out.tag("enabled",origin(md,ot + "enabled"),"false");
687 
688             //multipart-config
689             MultipartConfigElement multipartConfig = ((ServletHolder.Registration)s.getRegistration()).getMultipartConfig();
690             if (multipartConfig != null)
691             {
692                 out.open("multipart-config", origin(md, s.getName()+".servlet.multipart-config"));
693                 if (multipartConfig.getLocation() != null)
694                     out.tag("location", multipartConfig.getLocation());
695                 out.tag("max-file-size", Long.toString(multipartConfig.getMaxFileSize()));
696                 out.tag("max-request-size", Long.toString(multipartConfig.getMaxRequestSize()));
697                 out.tag("file-size-threshold", Long.toString(multipartConfig.getFileSizeThreshold()));
698                 out.close();
699             }
700         }
701 
702         out.tag("async-supported",origin(md,ot + "async-supported"),holder.isAsyncSupported()?"true":"false");
703         out.close();
704     }
705 
706     public static Map<String, String> origin(MetaData md, String name)
707     {
708         if (!LOG.isDebugEnabled())
709             return Collections.emptyMap();
710         if (name == null)
711             return Collections.emptyMap();
712         OriginInfo origin = md.getOriginInfo(name);
713         // System.err.println("origin of "+name+" is "+origin);
714         if (origin == null)
715             return Collections.emptyMap();
716         return Collections.singletonMap("origin",origin.toString());
717 
718     }
719     
720 }