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