Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / apphosting / utils / config / AppYaml.java
blob459a47524b944042cba1df12b3c054d945de6b56
1 // Copyright 2010 Google. All Rights Reserved.
2 package com.google.apphosting.utils.config;
4 import static com.google.common.collect.Maps.newLinkedHashMapWithExpectedSize;
6 import com.google.common.base.Preconditions;
7 import com.google.common.xml.XmlEscapers;
9 import net.sourceforge.yamlbeans.YamlConfig;
10 import net.sourceforge.yamlbeans.YamlException;
11 import net.sourceforge.yamlbeans.YamlReader;
13 import java.io.PrintWriter;
14 import java.io.Reader;
15 import java.io.StringReader;
16 import java.io.Writer;
17 import java.util.LinkedHashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.TreeMap;
22 /**
23 * JavaBean representation of the Java app.yaml file.
26 public class AppYaml {
28 /**
29 * Plugin service to modify app.yaml with runtime-specific defaults.
31 public static interface Plugin {
32 AppYaml process(AppYaml yaml);
35 /**
36 * A {@code Handler} element from app.yaml. Maps to {@code servlet}, {@code servlet-mapping},
37 * {@code filter}, and {@code filter-mapping} elements in web.xml
40 public static class Handler {
42 public static enum Type {SERVLET, JSP, FILTER, NONE}
44 private String url;
45 private String jsp;
46 private String servlet;
47 private String filter;
48 private LoginType login;
49 private Security secure;
50 private Map<String, String> init_params;
51 private String name;
52 private boolean load_on_startup;
54 public enum LoginType { admin, required }
55 public enum Security { always, optional, never }
56 private boolean api_endpoint = false;
58 private String script;
60 private static final String MULTIPLE_HANDLERS = "Cannot set both %s and %s for the same url.";
62 public String getUrl() {
63 return url;
66 public void setUrl(String url) {
67 YamlUtils.validateUrl(url);
68 this.url = url;
71 public String getJsp() {
72 return jsp;
75 public void setJsp(String jsp) {
76 this.jsp = jsp;
77 checkHandlers();
80 public String getServlet() {
81 return servlet;
84 public void setServlet(String servlet) {
85 this.servlet = servlet;
86 checkHandlers();
89 public String getFilter() {
90 return filter;
93 public void setFilter(String filter) {
94 this.filter = filter;
95 checkHandlers();
98 public Type getType() {
99 if (servlet != null) {
100 return Type.SERVLET;
102 if (filter != null) {
103 return Type.FILTER;
105 if (jsp != null) {
106 return Type.JSP;
108 return Type.NONE;
111 public String getTarget() {
112 if (servlet != null) {
113 return servlet;
115 if (filter != null) {
116 return filter;
118 if (jsp != null) {
119 return jsp;
121 return null;
124 public void setScript(String script) {
125 this.script = script;
128 public String getScript() {
129 return this.script;
132 public LoginType getLogin() {
133 return login;
136 public void setLogin(LoginType login) {
137 this.login = login;
140 public Security getSecure() {
141 return secure;
144 public void setSecure(Security secure) {
145 if (secure == Security.never) {
146 throw new AppEngineConfigException("Java does not support secure: never");
148 this.secure = secure;
151 public Map<String, String> getInit_params() {
152 return init_params;
155 public void setInit_params(Map<String, String> init_params) {
156 if (init_params == null) {
157 this.init_params = null;
158 } else {
159 this.init_params = new TreeMap<String, String>(init_params);
163 public String getName() {
164 return (name == null ? getTarget() : name);
167 public void setLoad_on_startup(boolean loadOnStartup) {
168 this.load_on_startup = loadOnStartup;
171 public boolean getLoad_on_startup() {
172 return this.load_on_startup;
175 public void setName(String name) {
176 this.name = name;
179 public String getApi_endpoint() {
180 return "" + this.api_endpoint;
183 public void setApi_endpoint(String api_endpoint) {
184 this.api_endpoint = YamlUtils.parseBoolean(api_endpoint);
187 public boolean isApiEndpoint() {
188 return this.api_endpoint;
191 private void checkHandlers() {
192 if (jsp != null && servlet != null) {
193 throw new AppEngineConfigException(String.format(MULTIPLE_HANDLERS, "jsp", "servlet"));
195 if (jsp != null && filter != null) {
196 throw new AppEngineConfigException(String.format(MULTIPLE_HANDLERS, "jsp", "filter"));
198 if (filter != null && servlet != null) {
199 throw new AppEngineConfigException(String.format(MULTIPLE_HANDLERS, "filter", "servlet"));
204 * Generates the {@code servlet} or {@code filter} element of web.xml
205 * corresponding to this handler.
207 private void generateDefinitionXml(XmlWriter xml) {
208 if (getServlet() != null || getJsp() != null) {
209 generateServletDefinition(xml);
210 } else if (getFilter() != null) {
211 generateFilterDefintion(xml);
215 private void generateMappingXml(XmlWriter xml) {
216 if (getServlet() != null || getJsp() != null) {
217 generateServletMapping(xml);
218 } else if (getFilter() != null) {
219 generateFilterMapping(xml);
221 generateSecurityConstraints(xml);
224 private void generateSecurityConstraints(XmlWriter xml) {
225 if (secure == Security.always || login == LoginType.required || login == LoginType.admin) {
226 xml.startElement("security-constraint");
227 xml.startElement("web-resource-collection");
228 xml.simpleElement("web-resource-name", "aname");
229 xml.simpleElement("url-pattern", getUrl());
230 xml.endElement("web-resource-collection");
231 if (login == LoginType.required) {
232 securityConstraint(xml, "auth", "role-name", "*");
233 } else if (login == LoginType.admin) {
234 securityConstraint(xml, "auth", "role-name", "admin");
236 if (secure == Security.always) {
237 securityConstraint(xml, "user-data", "transport-guarantee", "CONFIDENTIAL");
239 xml.endElement("security-constraint");
243 private void securityConstraint(XmlWriter xml, String type, String name, String value) {
244 type = type + "-constraint";
245 xml.startElement(type);
246 xml.simpleElement(name, value);
247 xml.endElement(type);
251 * Generates a {@code filter} element of web.xml corresponding to this handler.
253 private void generateFilterDefintion(XmlWriter xml) {
254 xml.startElement("filter");
255 xml.simpleElement("filter-name", getName());
256 xml.simpleElement("filter-class", getFilter());
257 generateInitParams(xml);
258 xml.endElement("filter");
262 * Generates a {@code filter-mapping} element of web.xml corresponding to this handler.
264 private void generateFilterMapping(XmlWriter xml) {
265 xml.startElement("filter-mapping");
266 xml.simpleElement("filter-name", getName());
267 xml.simpleElement("url-pattern", getUrl());
268 xml.endElement("filter-mapping");
272 * Generates a {@code servlet} or {@code jsp-file} element of web.xml corresponding to this
273 * handler.
275 private void generateServletDefinition(XmlWriter xml) {
276 xml.startElement("servlet");
277 xml.simpleElement("servlet-name", getName());
278 if (getJsp() == null) {
279 xml.simpleElement("servlet-class", getServlet());
280 } else {
281 xml.simpleElement("jsp-file", getJsp());
283 generateInitParams(xml);
284 if (load_on_startup) {
285 xml.simpleElement("load-on-startup", "1");
287 xml.endElement("servlet");
291 * Merges another handler into this handler, assuming that the other handler
292 * has the same name, type and target. This operation is intended to be
293 * used for generating a Servlet or Filter *definition* as opposed to a
294 * mapping, and therefore the urls of this handler and the other handler
295 * are not involved in the merge operation. The load_on_startup values
296 * of the two handlers will be OR'd and the init_params will be unioned.
298 public void mergeDefinitions(Handler otherHandler) {
299 Preconditions.checkArgument(this.getName().equals(otherHandler.getName()),
300 "Cannot merge handler named " + this.getName() + " with handler named " +
301 otherHandler.getName());
302 Preconditions.checkArgument(this.getType() == otherHandler.getType(),
303 "Cannot merge handler of type " + this.getType() + " with handler of type "
304 + otherHandler.getType());
305 Preconditions.checkArgument(this.getTarget().equals(otherHandler.getTarget()),
306 "Cannont merge handler with target " + this.getTarget() + " with handler with target "
307 + otherHandler.getTarget());
308 this.load_on_startup = this.load_on_startup || otherHandler.load_on_startup;
309 Map<String, String> mergedInitParams = new LinkedHashMap<String, String>();
310 if (this.init_params != null) {
311 mergedInitParams.putAll(this.init_params);
313 if (otherHandler.init_params != null) {
314 for (String key : otherHandler.init_params.keySet()) {
315 String thisValue = mergedInitParams.get(key);
316 String otherValue = otherHandler.init_params.get(key);
317 if (thisValue == null) {
318 mergedInitParams.put(key, otherValue);
319 } else if (!thisValue.equals(otherValue)) {
320 throw new IllegalArgumentException(
321 "Cannot merge handlers with conflicting values for the init_param: " + key + " : "
322 + thisValue + " vs " + otherValue);
326 if (mergedInitParams.size() != 0) {
327 this.init_params = mergedInitParams;
332 * Generates a {@code servlet-mapping} element of web.xml corresponding to this handler.
334 private void generateServletMapping(XmlWriter xml) {
335 if (isApiEndpoint()) {
336 xml.startElement("servlet-mapping", "id", xml.nextApiEndpointId());
337 } else {
338 xml.startElement("servlet-mapping");
340 xml.simpleElement("servlet-name", getName());
341 xml.simpleElement("url-pattern", getUrl());
342 xml.endElement("servlet-mapping");
345 private void generateInitParams(XmlWriter xml) {
346 if (init_params != null) {
347 for (Map.Entry<String, String> param : init_params.entrySet()) {
348 xml.startElement("init-param");
349 xml.simpleElement("param-name", param.getKey());
350 xml.simpleElement("param-value", param.getValue());
351 xml.endElement("init-param");
356 private void generateEndpointServletMappingId(XmlWriter xml) {
357 if (isApiEndpoint()) {
358 xml.simpleElement("endpoint-servlet-mapping-id", xml.nextApiEndpointId());
363 public static class ResourceFile {
364 private static final String EMPTY_MESSAGE = "Missing include or exclude.";
365 private static final String BOTH_MESSAGE = "Cannot specify both include and exclude.";
367 protected String include;
368 protected String exclude;
369 protected Map<String, String> httpHeaders;
371 public String getInclude() {
372 if (exclude == null && include == null) {
373 throw new AppEngineConfigException(EMPTY_MESSAGE);
375 return include;
378 public void setInclude(String include) {
379 if (exclude != null) {
380 throw new AppEngineConfigException(BOTH_MESSAGE);
382 this.include = include;
385 public String getExclude() {
386 if (exclude == null && include == null) {
387 throw new AppEngineConfigException(EMPTY_MESSAGE);
389 return exclude;
392 public void setExclude(String exclude) {
393 if (include != null) {
394 throw new AppEngineConfigException(BOTH_MESSAGE);
396 this.exclude = exclude;
399 public Map<String, String> getHttp_headers() {
400 if (include == null) {
401 throw new AppEngineConfigException("Missing include.");
404 return httpHeaders;
407 public void setHttp_headers(Map<String, String> httpHeaders) {
408 if (include == null) {
409 throw new AppEngineConfigException("Missing include.");
412 this.httpHeaders = httpHeaders;
416 public static class StaticFile extends ResourceFile {
417 private static final String NO_INCLUDE = "Missing include.";
418 private static final String INCLUDE_ONLY = "Expiration can only be specified with include.";
419 private String expiration;
421 public String getExpiration() {
422 if (expiration != null && include == null) {
423 throw new AppEngineConfigException(NO_INCLUDE);
425 return expiration;
428 public void setExpiration(String expiration) {
429 if (exclude != null) {
430 throw new AppEngineConfigException(INCLUDE_ONLY);
432 this.expiration = expiration;
435 @Override
436 public void setExclude(String exclude) {
437 if (expiration != null) {
438 throw new AppEngineConfigException(INCLUDE_ONLY);
440 super.setExclude(exclude);
444 public static class AdminConsole {
445 private List<AdminPage> pages;
447 public List<AdminPage> getPages() {
448 return pages;
451 public void setPages(List<AdminPage> pages) {
452 this.pages = pages;
456 public static class AdminPage {
457 private String name;
458 private String url;
460 public String getName() {
461 return name;
464 public void setName(String name) {
465 this.name = name;
468 public String getUrl() {
469 return url;
472 public void setUrl(String url) {
473 this.url = url;
477 public static class AsyncSessionPersistence {
478 private boolean enabled = false;
479 private String queue_name;
481 public String getEnabled() {
482 return "" + enabled;
485 public void setEnabled(String enabled) {
486 this.enabled = YamlUtils.parseBoolean(enabled);
489 public String getQueue_name() {
490 return this.queue_name;
493 public void setQueue_name(String queue_name) {
494 this.queue_name = queue_name;
498 public static class ErrorHandler {
499 private String file;
500 private String errorCode;
502 public String getFile() {
503 return file;
506 public void setFile(String file) {
507 this.file = file;
510 public String getError_code() {
511 return errorCode;
514 public void setError_code(String errorCode) {
515 this.errorCode = errorCode;
520 * AutomaticScaling bean.
522 public static class AutomaticScaling {
523 private String minPendingLatency;
524 private String maxPendingLatency;
525 private String minIdleInstances;
526 private String maxIdleInstances;
527 private String maxConcurrentRequests;
529 public String getMin_pending_latency() {
530 return minPendingLatency;
533 public void setMin_pending_latency(String minPendingLatency) {
534 this.minPendingLatency = minPendingLatency;
537 public String getMax_pending_latency() {
538 return maxPendingLatency;
541 public void setMax_pending_latency(String maxPendingLatency) {
542 this.maxPendingLatency = maxPendingLatency;
545 public String getMin_idle_instances() {
546 return minIdleInstances;
549 public void setMin_idle_instances(String minIdleInstances) {
550 this.minIdleInstances = minIdleInstances;
553 public String getMax_idle_instances() {
554 return maxIdleInstances;
557 public void setMax_idle_instances(String maxIdleInstances) {
558 this.maxIdleInstances = maxIdleInstances;
561 public String getMax_concurrent_requests() {
562 return maxConcurrentRequests;
565 public void setMax_concurrent_requests(String maxConcurrentRequests) {
566 this.maxConcurrentRequests = maxConcurrentRequests;
571 * ManualScaling bean.
573 public static class ManualScaling {
574 private String instances;
576 public String getInstances() {
577 return instances;
580 public void setInstances(String instances) {
581 this.instances = instances;
586 * BasicScaling bean.
588 public static class BasicScaling {
589 private String maxInstances;
590 private String idleTimeout;
592 public String getMax_instances() {
593 return maxInstances;
596 public void setMax_instances(String maxInstances) {
597 this.maxInstances = maxInstances;
599 public String getIdle_timeout() {
600 return idleTimeout;
603 public void setIdle_timeout(String idleTimeout) {
604 this.idleTimeout = idleTimeout;
608 private String application;
609 private String version;
610 private String source_language;
611 private String module;
612 private String instanceClass;
613 private AutomaticScaling automatic_scaling;
614 private ManualScaling manual_scaling;
615 private BasicScaling basic_scaling;
616 private String runtime;
617 private List<Handler> handlers;
618 private String public_root;
619 private List<StaticFile> static_files;
620 private List<ResourceFile> resource_files;
621 private boolean ssl_enabled = true;
622 private boolean precompilation_enabled = true;
623 private boolean sessions_enabled = false;
624 private AsyncSessionPersistence async_session_persistence;
625 private boolean threadsafe = false;
626 private String auto_id_policy;
627 private boolean threadsafeWasSet = false;
628 private boolean codeLock = false;
629 private Map<String, String> system_properties;
630 private Map<String, String> env_variables;
631 private Map<String, String> context_params;
632 private List<String> welcome_files;
633 private List<String> listeners;
634 private List<String> inbound_services;
635 private AdminConsole admin_console;
636 private List<ErrorHandler> error_handlers;
637 private ApiConfig api_config;
638 private Pagespeed pagespeed;
639 private String web_xml;
641 private static final String REQUIRED_FIELD = "Missing required element '%s'.";
643 public String getApplication() {
644 if (application == null) {
645 throw new AppEngineConfigException(String.format(REQUIRED_FIELD, "application"));
647 return application;
650 public void setApplication(String application) {
651 this.application = application;
654 public String getVersion() {
655 return version;
658 public void setVersion(String version) {
659 this.version = version;
662 public void setSource_language(String sourceLanguage) {
663 this.source_language = sourceLanguage;
666 public String getSource_language() {
667 return source_language;
670 public String getModule() {
671 return module;
674 public void setModule(String module) {
675 this.module = module;
678 public String getInstance_class() {
679 return instanceClass;
682 public void setInstance_class(String instanceClass) {
683 this.instanceClass = instanceClass;
686 public AutomaticScaling getAutomatic_scaling() {
687 return automatic_scaling;
690 public void setAutomatic_scaling(AutomaticScaling automaticScaling) {
691 this.automatic_scaling = automaticScaling;
694 public ManualScaling getManual_scaling() {
695 return manual_scaling;
698 public void setManual_scaling(ManualScaling manualScaling) {
699 this.manual_scaling = manualScaling;
702 public BasicScaling getBasic_scaling() {
703 return basic_scaling;
706 public void setBasic_scaling(BasicScaling basicScaling) {
707 this.basic_scaling = basicScaling;
710 public String getRuntime() {
711 return runtime;
714 public void setRuntime(String runtime) {
715 this.runtime = runtime;
718 public List<Handler> getHandlers() {
719 return handlers;
722 public void setHandlers(List<Handler> handlers) {
723 this.handlers = handlers;
724 if (this.api_config != null) {
725 this.api_config.setHandlers(handlers);
729 public String getPublic_root() {
730 return public_root;
733 public void setPublic_root(String public_root) {
734 this.public_root = public_root;
737 public List<StaticFile> getStatic_files() {
738 return static_files;
741 public void setStatic_files(List<StaticFile> static_files) {
742 this.static_files = static_files;
745 public List<ResourceFile> getResource_files() {
746 return resource_files;
749 public void setResource_files(List<ResourceFile> resource_files) {
750 this.resource_files = resource_files;
753 public String getSsl_enabled() {
754 return "" + ssl_enabled;
757 public void setSsl_enabled(String ssl_enabled) {
758 this.ssl_enabled = YamlUtils.parseBoolean(ssl_enabled);
761 public boolean isSslEnabled() {
762 return ssl_enabled;
765 public String getPrecompilation_enabled() {
766 return "" + precompilation_enabled;
769 public boolean isPrecompilationEnabled() {
770 return precompilation_enabled;
773 public void setPrecompilation_enabled(String precompilation_enabled) {
774 this.precompilation_enabled = YamlUtils.parseBoolean(precompilation_enabled);
777 public String getSessions_enabled() {
778 return "" + sessions_enabled;
781 public boolean isSessionsEnabled() {
782 return sessions_enabled;
785 public void setSessions_enabled(String sessions_enabled) {
786 this.sessions_enabled = YamlUtils.parseBoolean(sessions_enabled);
789 public AsyncSessionPersistence getAsync_session_persistence() {
790 return async_session_persistence;
793 public void setAsync_session_persistence(AsyncSessionPersistence async_session_persistence) {
794 this.async_session_persistence = async_session_persistence;
797 public String getThreadsafe() {
798 return "" + threadsafe;
801 public boolean isThreadsafeSet() {
802 return threadsafeWasSet;
805 public void setThreadsafe(String threadsafe) {
806 this.threadsafe = YamlUtils.parseBoolean(threadsafe);
807 threadsafeWasSet = true;
810 public String getAuto_id_policy() {
811 return auto_id_policy;
814 public void setAuto_id_policy(String policy) {
815 auto_id_policy = policy;
818 public String getCode_lock() {
819 return "" + codeLock;
822 public void setCode_lock(String codeLock) {
823 this.codeLock = YamlUtils.parseBoolean(codeLock);
826 public Map<String, String> getSystem_properties() {
827 return system_properties;
830 public void setSystem_properties(Map<String, String> system_properties) {
831 this.system_properties = system_properties;
834 public Map<String, String> getEnv_variables() {
835 return env_variables;
838 public void setEnv_variables(Map<String, String> env_variables) {
839 this.env_variables = env_variables;
842 public List<String> getWelcome_files() {
843 return welcome_files;
846 public void setWelcome_files(List<String> welcome_files) {
847 this.welcome_files = welcome_files;
850 public Map<String, String> getContext_params() {
851 return context_params;
854 public void setContext_params(Map<String, String> context_params) {
855 this.context_params = context_params;
858 public List<String> getListeners() {
859 return listeners;
862 public void setListeners(List<String> listeners) {
863 this.listeners = listeners;
866 public String getWeb_xml() {
867 return web_xml;
870 public void setWeb_xml(String web_xml) {
871 this.web_xml = web_xml;
874 public List<String> getInbound_services() {
875 return inbound_services;
878 public void setInbound_services(List<String> inbound_services) {
879 this.inbound_services = inbound_services;
882 public AdminConsole getAdmin_console() {
883 return admin_console;
886 public void setAdmin_console(AdminConsole admin_console) {
887 this.admin_console = admin_console;
890 public List<ErrorHandler> getError_handlers() {
891 return error_handlers;
894 public void setError_handlers(List<ErrorHandler> error_handlers) {
895 this.error_handlers = error_handlers;
898 public ApiConfig getApi_config() {
899 return api_config;
902 public void setApi_config(ApiConfig api_config) {
903 this.api_config = api_config;
904 if (handlers != null) {
905 this.api_config.setHandlers(handlers);
909 public Pagespeed getPagespeed() {
910 return pagespeed;
913 public void setPagespeed(Pagespeed pagespeed) {
914 this.pagespeed = pagespeed;
917 public AppYaml applyPlugins() {
918 AppYaml yaml = this;
919 for (Plugin plugin : PluginLoader.loadPlugins(Plugin.class)) {
920 AppYaml modified = plugin.process(yaml);
921 if (modified != null) {
922 yaml = modified;
925 return yaml;
929 * Represents an api-config: top level app.yaml stanza
930 * This is a singleton specifying url: and servlet: for the api config server.
932 public static class ApiConfig {
933 private String url;
934 private String servlet;
935 private List<Handler> handlers;
937 public void setHandlers(List<Handler> handlers) {
938 this.handlers = handlers;
941 public String getUrl() {
942 return url;
945 public void setUrl(String url) {
946 YamlUtils.validateUrl(url);
947 this.url = url;
950 public String getServlet() {
951 return servlet;
954 public void setServlet(String servlet) {
955 this.servlet = servlet;
958 private void generateXml(XmlWriter xml) {
959 xml.startElement("api-config", "servlet-class", getServlet(), "url-pattern", getUrl());
960 if (handlers != null) {
961 for (Handler handler : handlers) {
962 handler.generateEndpointServletMappingId(xml);
965 xml.endElement("api-config");
970 * Represents a &lt;pagespeed&gt; element. This is used to specify configuration for the Page
971 * Speed Service, which can be used to automatically optimize the loading speed of app engine
972 * sites.
974 public static class Pagespeed {
975 private List<String> urlBlacklist;
976 private List<String> domainsToRewrite;
977 private List<String> enabledRewriters;
978 private List<String> disabledRewriters;
980 public void setUrl_blacklist(List<String> urls) {
981 urlBlacklist = urls;
984 public List<String> getUrl_blacklist() {
985 return urlBlacklist;
988 public void setDomains_to_rewrite(List<String> domains) {
989 domainsToRewrite = domains;
992 public List<String> getDomains_to_rewrite() {
993 return domainsToRewrite;
996 public void setEnabled_rewriters(List<String> rewriters) {
997 enabledRewriters = rewriters;
1000 public List<String> getEnabled_rewriters() {
1001 return enabledRewriters;
1004 public void setDisabled_rewriters(List<String> rewriters) {
1005 disabledRewriters = rewriters;
1008 public List<String> getDisabled_rewriters() {
1009 return disabledRewriters;
1012 private void generateXml(XmlWriter xml) {
1013 if (!isEmpty()) {
1014 xml.startElement("pagespeed");
1015 appendElements(xml, "url-blacklist", urlBlacklist);
1016 appendElements(xml, "domain-to-rewrite", domainsToRewrite);
1017 appendElements(xml, "enabled-rewriter", enabledRewriters);
1018 appendElements(xml, "disabled-rewriter", disabledRewriters);
1019 xml.endElement("pagespeed");
1023 private void appendElements(XmlWriter xml, String name, List<String> l) {
1024 if (l != null) {
1025 for (String elt : l) {
1026 xml.simpleElement(name, elt);
1031 private boolean isEmpty() {
1032 return !hasElements(urlBlacklist) && !hasElements(domainsToRewrite)
1033 && !hasElements(enabledRewriters) && !hasElements(disabledRewriters);
1036 private boolean hasElements(List<?> coll) {
1037 return coll != null && !coll.isEmpty();
1041 private class XmlWriter {
1042 private static final String XML_HEADER = "<!-- Generated from app.yaml. Do not edit. -->";
1043 private final PrintWriter writer;
1044 private int indent = 0;
1045 private int apiEndpointId = 0;
1047 public XmlWriter(Writer w) {
1048 writer = new PrintWriter(w);
1049 writer.println(XML_HEADER);
1052 public void startElement(String name, String... attributes) {
1053 startElement(name, false, attributes);
1054 writer.println();
1057 public void startElement(String name, boolean empty, String... attributes) {
1058 indent();
1059 writer.print("<");
1060 writer.print(name);
1061 for (int i = 0; i < attributes.length; i += 2) {
1062 String attributeName = attributes[i];
1063 String value = attributes[i + 1];
1064 if (value != null) {
1065 writer.print(" ");
1066 writer.print(attributeName);
1067 writer.print("='");
1068 writer.print(escapeAttribute(value));
1069 writer.print("'");
1072 if (empty) {
1073 writer.println("/>");
1074 } else {
1075 writer.print(">");
1076 indent += 2;
1080 public void endElement(String name) {
1081 endElement(name, true);
1084 public void endElement(String name, boolean needIndent) {
1085 indent -= 2;
1086 if (needIndent) {
1087 indent();
1089 writer.print("</");
1090 writer.print(name);
1091 writer.println(">");
1094 public void emptyElement(String name, String... attributes) {
1095 startElement(name, true, attributes);
1098 public void simpleElement(String name, String value, String... attributes) {
1099 startElement(name, false, attributes);
1100 writer.print(escapeContent(value));
1101 endElement(name, false);
1104 public void writeUnescaped(String xmlContent) {
1105 writer.println(xmlContent);
1108 private void indent() {
1109 for (int i = 0; i < indent; i++) {
1110 writer.print(" ");
1114 private String escapeContent(String value) {
1115 if (value == null) {
1116 return null;
1118 return XmlEscapers.xmlContentEscaper().escape(value);
1121 private String escapeAttribute(String value) {
1122 if (value == null) {
1123 return null;
1125 return XmlEscapers.xmlAttributeEscaper().escape(value);
1128 private String nextApiEndpointId() {
1129 return String.format("endpoint-%1$d", ++apiEndpointId);
1133 private void addOptionalElement(XmlWriter xml, String name, String value) {
1134 if (value != null) {
1135 xml.simpleElement(name, value);
1139 public void generateAppEngineWebXml(Writer writer) {
1140 XmlWriter xml = new XmlWriter(writer);
1141 xml.startElement("appengine-web-app", "xmlns", "http://appengine.google.com/ns/1.0");
1142 xml.simpleElement("application", getApplication());
1143 addOptionalElement(xml, "version", getVersion());
1144 addOptionalElement(xml, "source-language", getSource_language());
1145 addOptionalElement(xml, "module", getModule());
1146 addOptionalElement(xml, "instance-class", getInstance_class());
1147 addOptionalElement(xml, "public-root", public_root);
1148 addOptionalElement(xml, "auto-id-policy", getAuto_id_policy());
1149 if (automatic_scaling != null) {
1150 xml.startElement("automatic-scaling");
1151 addOptionalElement(xml, "min-pending-latency", automatic_scaling.getMin_pending_latency());
1152 addOptionalElement(xml, "max-pending-latency", automatic_scaling.getMax_pending_latency());
1153 addOptionalElement(xml, "min-idle-instances", automatic_scaling.getMin_idle_instances());
1154 addOptionalElement(xml, "max-idle-instances", automatic_scaling.getMax_idle_instances());
1155 addOptionalElement(xml, "max-concurrent-requests",
1156 automatic_scaling.getMax_concurrent_requests());
1157 xml.endElement("automatic-scaling");
1159 if (manual_scaling != null) {
1160 xml.startElement("manual-scaling");
1161 xml.simpleElement("instances", manual_scaling.getInstances());
1162 xml.endElement("manual-scaling");
1164 if (basic_scaling != null) {
1165 xml.startElement("basic-scaling");
1166 xml.simpleElement("max-instances", basic_scaling.getMax_instances());
1167 addOptionalElement(xml, "idle-timeout", basic_scaling.getIdle_timeout());
1168 xml.endElement("basic-scaling");
1170 xml.startElement("static-files");
1171 if (static_files != null) {
1172 for (StaticFile file : static_files) {
1173 String name, path;
1174 if (file.getInclude() != null) {
1175 generateInclude(file, xml);
1176 } else {
1177 xml.emptyElement("exclude", "path", file.getExclude());
1181 xml.endElement("static-files");
1182 xml.startElement("resource-files");
1183 if (resource_files != null) {
1184 for (ResourceFile file : resource_files) {
1185 String name, path;
1186 if (file.getInclude() != null) {
1187 name = "include";
1188 path = file.getInclude();
1189 } else {
1190 name = "exclude";
1191 path = file.getExclude();
1193 xml.emptyElement(name, "path", path);
1196 xml.endElement("resource-files");
1197 xml.simpleElement("ssl-enabled", getSsl_enabled());
1198 xml.simpleElement("precompilation-enabled", getPrecompilation_enabled());
1199 if (isThreadsafeSet()) {
1200 xml.simpleElement("threadsafe", getThreadsafe());
1202 xml.simpleElement("code-lock", getCode_lock());
1203 xml.simpleElement("sessions-enabled", getSessions_enabled());
1204 if (async_session_persistence != null) {
1205 xml.simpleElement("async-session-persistence", null,
1206 "enabled", getAsync_session_persistence().getEnabled(),
1207 "queue-name", getAsync_session_persistence().getQueue_name());
1209 if (system_properties != null) {
1210 xml.startElement("system-properties");
1211 for (Map.Entry<String, String> entry : system_properties.entrySet()) {
1212 xml.emptyElement("property", "name", entry.getKey(), "value", entry.getValue());
1214 xml.endElement("system-properties");
1216 if (env_variables != null) {
1217 xml.startElement("env-variables");
1218 for (Map.Entry<String, String> entry : env_variables.entrySet()) {
1219 xml.emptyElement("env-var", "name", entry.getKey(), "value", entry.getValue());
1221 xml.endElement("env-variables");
1223 boolean warmupService = false;
1224 if (inbound_services != null) {
1225 xml.startElement("inbound-services");
1226 for (String service : inbound_services) {
1227 if (AppEngineWebXml.WARMUP_SERVICE.equals(service)) {
1228 warmupService = true;
1229 } else {
1230 xml.simpleElement("service", service);
1233 xml.endElement("inbound-services");
1235 xml.simpleElement("warmup-requests-enabled", Boolean.toString(warmupService));
1236 if (admin_console != null && admin_console.getPages() != null) {
1237 xml.startElement("admin-console");
1238 for (AdminPage page : admin_console.getPages()) {
1239 xml.emptyElement("page", "name", page.getName(), "url", page.getUrl());
1241 xml.endElement("admin-console");
1243 if (error_handlers != null) {
1244 xml.startElement("static-error-handlers");
1245 for (ErrorHandler handler : error_handlers) {
1246 xml.emptyElement("handler",
1247 "file", handler.getFile(),
1248 "error-code", handler.getError_code());
1250 xml.endElement("static-error-handlers");
1252 if (api_config != null) {
1253 api_config.generateXml(xml);
1255 if (pagespeed != null) {
1256 pagespeed.generateXml(xml);
1258 xml.endElement("appengine-web-app");
1262 * Generates the {@code servlet}, {@code servlet-mapping}, {@code filter}, and
1263 * {@code filter-mapping} elements of web.xml corresponding to the {@link #handlers} list. There
1264 * may be multiple {@link Handler handlers} corresponding to the same servlet or filter, because a
1265 * single handler can only specify one URL pattern and the user may wish to map several URL
1266 * patterns to the same servlet or filter. In this case we want to have multiple
1267 * {@code servlet-mapping} or {@code filter-mapping} elements but only a single {@code servlet} or
1268 * {@code filter} element.
1270 private void generateHandlerXml(XmlWriter xmlWriter) {
1271 if (handlers == null) {
1272 return;
1274 Map<String, Handler> servletsByName = newLinkedHashMapWithExpectedSize(handlers.size());
1275 Map<String, Handler> filtersByName = newLinkedHashMapWithExpectedSize(handlers.size());
1276 for (Handler handler : handlers) {
1277 String name = handler.getName();
1278 if (name != null) {
1279 Handler.Type type = handler.getType();
1280 boolean isServlet = (type == Handler.Type.SERVLET || type == Handler.Type.JSP);
1281 boolean isFilter = (type == Handler.Type.FILTER);
1282 Handler existing = (isServlet ? servletsByName.get(name) : filtersByName.get(name));
1283 if (existing != null) {
1284 existing.mergeDefinitions(handler);
1285 } else {
1286 if (isServlet) {
1287 servletsByName.put(name, handler);
1289 if (isFilter) {
1290 filtersByName.put(name, handler);
1295 for (Handler handler : servletsByName.values()) {
1296 handler.generateDefinitionXml(xmlWriter);
1298 for (Handler handler : filtersByName.values()) {
1299 handler.generateDefinitionXml(xmlWriter);
1301 for (Handler handler : handlers) {
1302 handler.generateMappingXml(xmlWriter);
1306 public void generateWebXml(Writer writer) {
1307 XmlWriter xml = new XmlWriter(writer);
1308 xml.startElement("web-app", "version", "2.5",
1309 "xmlns", "http://java.sun.com/xml/ns/javaee",
1310 "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance",
1311 "xsi:schemaLocation",
1312 "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
1314 generateHandlerXml(xml);
1315 if (context_params != null) {
1316 for (Map.Entry<String, String> entry : context_params.entrySet()) {
1317 xml.startElement("context-param");
1318 xml.simpleElement("param-name", entry.getKey());
1319 xml.simpleElement("param-value", entry.getValue());
1320 xml.endElement("context-param");
1323 if (welcome_files != null) {
1324 xml.startElement("welcome-file-list");
1325 for (String file : welcome_files) {
1326 xml.simpleElement("welcome-file", file);
1328 xml.endElement("welcome-file-list");
1330 if (listeners != null) {
1331 for (String listener : listeners) {
1332 xml.startElement("listener");
1333 xml.simpleElement("listener-class", listener);
1334 xml.endElement("listener");
1337 if (web_xml != null) {
1338 xml.writeUnescaped(web_xml);
1340 xml.endElement("web-app");
1343 public static AppYaml parse(Reader reader) {
1344 YamlReader yaml = new YamlReader(reader);
1345 prepareParser(yaml.getConfig());
1346 try {
1347 AppYaml appYaml = yaml.read(AppYaml.class);
1348 if (appYaml == null) {
1349 throw new YamlException("Unable to parse yaml file");
1351 return appYaml.applyPlugins();
1352 } catch (YamlException e) {
1353 Throwable innerException = e.getCause();
1355 while (innerException != null) {
1356 if (innerException instanceof AppEngineConfigException) {
1357 throw (AppEngineConfigException) innerException;
1359 innerException = innerException.getCause();
1362 throw new AppEngineConfigException(e.getMessage(), e);
1366 public static AppYaml parse(String yaml) {
1367 return parse(new StringReader(yaml));
1370 public static void prepareParser(YamlConfig config) {
1371 config.setPropertyElementType(AppYaml.class, "handlers", Handler.class);
1372 config.setPropertyElementType(AppYaml.class, "static_files", StaticFile.class);
1373 config.setPropertyElementType(AppYaml.class, "resource_files", ResourceFile.class);
1374 config.setPropertyElementType(AppYaml.class, "system_properties", String.class);
1375 config.setPropertyElementType(AppYaml.class, "context_params", String.class);
1376 config.setPropertyElementType(AppYaml.class, "env_variables", String.class);
1377 config.setPropertyElementType(AppYaml.class, "welcome_files", String.class);
1378 config.setPropertyElementType(AppYaml.class, "listeners", String.class);
1379 config.setPropertyElementType(AppYaml.class, "inbound_services", String.class);
1380 config.setPropertyElementType(Handler.class, "init_params", String.class);
1381 config.setPropertyElementType(AdminConsole.class, "pages", AdminPage.class);
1382 config.setPropertyElementType(AppYaml.class, "error_handlers", ErrorHandler.class);
1383 config.setPropertyElementType(Pagespeed.class, "url_blacklist", String.class);
1384 config.setPropertyElementType(Pagespeed.class, "domains_to_rewrite", String.class);
1385 config.setPropertyElementType(Pagespeed.class, "enabled_rewriters", String.class);
1386 config.setPropertyElementType(Pagespeed.class, "disabled_rewriters", String.class);
1389 private void generateInclude(StaticFile include, XmlWriter xml) {
1390 String path = include.getInclude();
1391 Map<String, String> httpHeaders = include.getHttp_headers();
1392 if (httpHeaders == null || httpHeaders.isEmpty()) {
1393 xml.emptyElement("include",
1394 "path", include.getInclude(),
1395 "expiration", include.getExpiration());
1396 } else {
1397 xml.startElement("include",
1398 false,
1399 "path", include.getInclude(),
1400 "expiration", include.getExpiration());
1401 for (Map.Entry<String, String> entry : httpHeaders.entrySet()) {
1402 xml.emptyElement("http-header",
1403 "name", entry.getKey(),
1404 "value", entry.getValue());
1406 xml.endElement("include");