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