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.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
69
70
71
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
86
87
88 public QuickStartDescriptorGenerator (WebAppContext w, String extraXML)
89 {
90 _webApp = w;
91 _extraXML = extraXML;
92 }
93
94
95
96
97
98
99
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
131
132
133 String resourceBase = _webApp.getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString();
134
135
136 addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS);
137
138 addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS);
139
140 addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase);
141
142 addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase);
143
144
145
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
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
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
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
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
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
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
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
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
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
513
514
515
516
517
518 private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException
519 {
520 addContextParamFromAttribute(out,attribute,null);
521 }
522
523
524
525
526
527
528
529
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
561
562
563
564
565
566
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))
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
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
670
671
672
673
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 }