App Engine Java SDK version 1.9.25
[gae.git] / java / src / main / com / google / apphosting / utils / config / AppEngineWebXmlProcessor.java
blobe060a23be718064a9dcd8fc0738799b594305a54
1 // Copyright 2008 Google Inc. All Rights Reserved.
2 package com.google.apphosting.utils.config;
4 import com.google.apphosting.utils.config.AppEngineWebXml.AdminConsolePage;
5 import com.google.apphosting.utils.config.AppEngineWebXml.ApiConfig;
6 import com.google.apphosting.utils.config.AppEngineWebXml.AutomaticScaling;
7 import com.google.apphosting.utils.config.AppEngineWebXml.BasicScaling;
8 import com.google.apphosting.utils.config.AppEngineWebXml.ClassLoaderConfig;
9 import com.google.apphosting.utils.config.AppEngineWebXml.CpuUtilization;
10 import com.google.apphosting.utils.config.AppEngineWebXml.ErrorHandler;
11 import com.google.apphosting.utils.config.AppEngineWebXml.ManualScaling;
12 import com.google.apphosting.utils.config.AppEngineWebXml.Pagespeed;
13 import com.google.apphosting.utils.config.AppEngineWebXml.PrioritySpecifierEntry;
14 import com.google.apphosting.utils.config.AppEngineWebXml.HealthCheck;
15 import com.google.apphosting.utils.config.AppEngineWebXml.Resources;
16 import com.google.apphosting.utils.config.AppEngineWebXml.Network;
18 import org.mortbay.xml.XmlParser;
19 import org.mortbay.xml.XmlParser.Node;
20 import org.xml.sax.SAXException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
29 /**
30 * Constructs an {@link AppEngineWebXml} from an xml document corresponding to
31 * appengine-web.xsd. We use Jetty's {@link XmlParser} utility for
32 * convenience.
34 * @TODO(user): Add a real link to the xsd once it exists and do schema
35 * validation.
38 class AppEngineWebXmlProcessor {
40 enum FileType { STATIC, RESOURCE }
42 private static final Logger logger = Logger.getLogger(AppEngineWebXmlProcessor.class.getName());
44 /**
45 * Construct an {@link AppEngineWebXml} from the xml document
46 * identified by the provided {@link InputStream}.
48 * @param is The InputStream containing the xml we want to parse and process.
50 * @return Object representation of the xml document.
51 * @throws AppEngineConfigException If the input stream cannot be parsed.
53 public AppEngineWebXml processXml(InputStream is) {
54 XmlParser.Node config = getTopLevelNode(is);
55 AppEngineWebXml appEngineWebXml = new AppEngineWebXml();
56 appEngineWebXml.setWarmupRequestsEnabled(true);
57 for (Object o : config) {
58 if (!(o instanceof XmlParser.Node)) {
59 continue;
61 XmlParser.Node node = (XmlParser.Node) o;
62 processSecondLevelNode(node, appEngineWebXml);
64 checkScalingConstraints(appEngineWebXml);
65 return appEngineWebXml;
68 /**
69 * Given an AppEngineWebXml, ensure it has no more than one of the scaling options available.
71 * @throws AppEngineConfigException If there is more than one scaling option selected.
73 private static void checkScalingConstraints(AppEngineWebXml appEngineWebXml) {
74 int count = appEngineWebXml.getManualScaling().isEmpty() ? 0 : 1;
75 count += appEngineWebXml.getBasicScaling().isEmpty() ? 0 : 1;
76 count += appEngineWebXml.getAutomaticScaling().isEmpty() ? 0 : 1;
77 if (count > 1) {
78 throw new AppEngineConfigException(
79 "There may be only one of 'automatic-scaling', 'manual-scaling' or " +
80 "'basic-scaling' elements.");
84 /**
85 * Given an InputStream, create a Node corresponding to the top level xml
86 * element.
88 * @throws AppEngineConfigException If the input stream cannot be parsed.
90 XmlParser.Node getTopLevelNode(InputStream is) {
91 XmlParser xmlParser = new XmlParser();
92 try {
93 return xmlParser.parse(is);
94 } catch (IOException e) {
95 String msg = "Received IOException parsing the input stream.";
96 logger.log(Level.SEVERE, msg, e);
97 throw new AppEngineConfigException(msg, e);
98 } catch (SAXException e) {
99 String msg = "Received SAXException parsing the input stream.";
100 logger.log(Level.SEVERE, msg, e);
101 throw new AppEngineConfigException(msg, e);
105 private void processSecondLevelNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
106 String elementName = node.getTag();
107 if (elementName.equals("system-properties")) {
108 processSystemPropertiesNode(node, appEngineWebXml);
109 } else if (elementName.equals("vm-settings") || elementName.equals("beta-settings")) {
110 processBetaSettingsNode(node, appEngineWebXml);
111 } else if (elementName.equals("vm-health-check") || elementName.equals("health-check")) {
112 processHealthCheckNode(node, appEngineWebXml);
113 } else if (elementName.equals("resources")) {
114 processResourcesNode(node, appEngineWebXml);
115 } else if (elementName.equals("network")) {
116 processNetworkNode(node, appEngineWebXml);
117 } else if (elementName.equals("env-variables")) {
118 processEnvironmentVariablesNode(node, appEngineWebXml);
119 } else if (elementName.equals("application")) {
120 processApplicationNode(node, appEngineWebXml);
121 } else if (elementName.equals("version")) {
122 processVersionNode(node, appEngineWebXml);
123 } else if (elementName.equals("source-language")) {
124 processSourceLanguageNode(node, appEngineWebXml);
125 } else if (elementName.equals("module")) {
126 processModuleNode(node, appEngineWebXml);
127 } else if (elementName.equals("instance-class")) {
128 processInstanceClassNode(node, appEngineWebXml);
129 } else if (elementName.equals("automatic-scaling")) {
130 processAutomaticScalingNode(node, appEngineWebXml);
131 } else if (elementName.equals("manual-scaling")) {
132 processManualScalingNode(node, appEngineWebXml);
133 } else if (elementName.equals("basic-scaling")) {
134 processBasicScalingNode(node, appEngineWebXml);
135 } else if (elementName.equals("static-files")) {
136 processFilesetNode(node, appEngineWebXml, FileType.STATIC);
137 } else if (elementName.equals("resource-files")) {
138 processFilesetNode(node, appEngineWebXml, FileType.RESOURCE);
139 } else if (elementName.equals("ssl-enabled")) {
140 processSslEnabledNode(node, appEngineWebXml);
141 } else if (elementName.equals("sessions-enabled")) {
142 processSessionsEnabledNode(node, appEngineWebXml);
143 } else if (elementName.equals("async-session-persistence")) {
144 processAsyncSessionPersistenceNode(node, appEngineWebXml);
145 } else if (elementName.equals("user-permissions")) {
146 processPermissionsNode(node, appEngineWebXml);
147 } else if (elementName.equals("public-root")) {
148 processPublicRootNode(node, appEngineWebXml);
149 } else if (elementName.equals("inbound-services")) {
150 processInboundServicesNode(node, appEngineWebXml);
151 } else if (elementName.equals("precompilation-enabled")) {
152 processPrecompilationEnabledNode(node, appEngineWebXml);
153 } else if (elementName.equals("admin-console")) {
154 processAdminConsoleNode(node, appEngineWebXml);
155 } else if (elementName.equals("static-error-handlers")) {
156 processErrorHandlerNode(node, appEngineWebXml);
157 } else if (elementName.equals("warmup-requests-enabled")) {
158 processWarmupRequestsEnabledNode(node, appEngineWebXml);
159 } else if (elementName.equals("threadsafe")) {
160 processThreadsafeNode(node, appEngineWebXml);
161 } else if (elementName.equals("auto-id-policy")) {
162 processAutoIdPolicyNode(node, appEngineWebXml);
163 } else if (elementName.equals("code-lock")) {
164 processCodeLockNode(node, appEngineWebXml);
165 } else if (elementName.equals("vm")) {
166 processVmNode(node, appEngineWebXml);
167 } else if (elementName.equals("env")) {
168 processEnvNode(node, appEngineWebXml);
169 } else if (elementName.equals("api-config")) {
170 processApiConfigNode(node, appEngineWebXml);
171 } else if (elementName.equals("pagespeed")) {
172 processPagespeedNode(node, appEngineWebXml);
173 } else if (elementName.equals("class-loader-config")) {
174 processClassLoaderConfig(node, appEngineWebXml);
175 } else if (elementName.equals("url-stream-handler")) {
176 processUrlStreamHandler(node, appEngineWebXml);
177 } else if (elementName.equals("use-google-connector-j")) {
178 processUseGoogleConnectorJNode(node, appEngineWebXml);
179 } else {
180 throw new AppEngineConfigException("Unrecognized element <" + elementName + ">");
184 private void processApplicationNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
185 appEngineWebXml.setAppId(getTextNode(node));
188 private void processPublicRootNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
189 appEngineWebXml.setPublicRoot(getTextNode(node));
192 private void processVersionNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
193 appEngineWebXml.setMajorVersionId(getTextNode(node));
196 private void processSourceLanguageNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
197 appEngineWebXml.setSourceLanguage(getTextNode(node));
200 private void processModuleNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
201 appEngineWebXml.setModule(getTextNode(node));
204 private void processInstanceClassNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
205 appEngineWebXml.setInstanceClass(getTextNode(node));
208 private String getChildNodeText(XmlParser.Node parentNode, String childTag) {
209 String result = null;
210 XmlParser.Node node = parentNode.get(childTag);
211 if (node != null) {
212 result = (String) node.get(0);
214 return result;
217 private Integer getChildNodePositiveInteger(XmlParser.Node parentNode, String childTag) {
218 Integer result = null;
219 XmlParser.Node node = parentNode.get(childTag);
220 if (node != null && node.get(0) != null) {
221 String trimmedText = ((String) node.get(0)).trim();
222 if (!trimmedText.isEmpty()) {
223 try {
224 result = Integer.parseInt(trimmedText);
225 } catch (NumberFormatException ex) {
226 throw new AppEngineConfigException(childTag + " should only contain integers.");
229 if (result <= 0) {
230 throw new AppEngineConfigException(childTag + " should only contain positive integers.");
234 return result;
237 private Double getChildNodeDouble(XmlParser.Node parentNode, String childTag) {
238 Double result = null;
239 XmlParser.Node node = parentNode.get(childTag);
240 if (node != null && node.get(0) != null) {
241 String trimmedText = ((String) node.get(0)).trim();
242 if (!trimmedText.isEmpty()) {
243 try {
244 result = Double.parseDouble(trimmedText);
245 } catch (NumberFormatException ex) {
246 throw new AppEngineConfigException(childTag + " should only contain doubles.");
247 } catch (NullPointerException ex) {
248 throw new AppEngineConfigException(childTag + " should NOT be empty.");
252 return result;
255 private void processAutomaticScalingNode(XmlParser.Node settingsNode,
256 AppEngineWebXml appEngineWebXml) {
257 AutomaticScaling automaticScaling = appEngineWebXml.getAutomaticScaling();
258 automaticScaling.setMinPendingLatency(getChildNodeText(settingsNode, "min-pending-latency"));
259 automaticScaling.setMaxPendingLatency(getChildNodeText(settingsNode, "max-pending-latency"));
260 automaticScaling.setMinIdleInstances(getChildNodeText(settingsNode, "min-idle-instances"));
261 automaticScaling.setMaxIdleInstances(getChildNodeText(settingsNode, "max-idle-instances"));
262 automaticScaling.setMaxConcurrentRequests(
263 getChildNodeText(settingsNode, "max-concurrent-requests"));
264 automaticScaling.setMinNumInstances(
265 getChildNodePositiveInteger(settingsNode, "min-num-instances"));
266 automaticScaling.setMaxNumInstances(
267 getChildNodePositiveInteger(settingsNode, "max-num-instances"));
268 automaticScaling.setCoolDownPeriodSec(
269 getChildNodePositiveInteger(settingsNode, "cool-down-period-sec"));
270 processCpuUtilizationNode(settingsNode, automaticScaling);
271 automaticScaling.setTargetNetworkSentBytesPerSec(
272 getChildNodePositiveInteger(settingsNode, "target-network-sent-bytes-per-sec"));
273 automaticScaling.setTargetNetworkSentPacketsPerSec(
274 getChildNodePositiveInteger(settingsNode, "target-network-sent-packets-per-sec"));
275 automaticScaling.setTargetNetworkReceivedBytesPerSec(
276 getChildNodePositiveInteger(settingsNode, "target-network-received-bytes-per-sec"));
277 automaticScaling.setTargetNetworkReceivedPacketsPerSec(
278 getChildNodePositiveInteger(settingsNode, "target-network-received-packets-per-sec"));
279 automaticScaling.setTargetDiskWriteBytesPerSec(
280 getChildNodePositiveInteger(settingsNode, "target-disk-write-bytes-per-sec"));
281 automaticScaling.setTargetDiskWriteOpsPerSec(
282 getChildNodePositiveInteger(settingsNode, "target-disk-write-ops-per-sec"));
283 automaticScaling.setTargetDiskReadBytesPerSec(
284 getChildNodePositiveInteger(settingsNode, "target-disk-read-bytes-per-sec"));
285 automaticScaling.setTargetDiskReadOpsPerSec(
286 getChildNodePositiveInteger(settingsNode, "target-disk-read-ops-per-sec"));
287 automaticScaling.setTargetRequestCountPerSec(
288 getChildNodePositiveInteger(settingsNode, "target-request-count-per-sec"));
289 automaticScaling.setTargetConcurrentRequests(
290 getChildNodePositiveInteger(settingsNode, "target-concurrent-requests"));
293 private void processCpuUtilizationNode(
294 XmlParser.Node settingsNode, AutomaticScaling automaticScaling) {
295 XmlParser.Node childNode = settingsNode.get("cpu-utilization");
296 if (childNode != null) {
297 CpuUtilization cpuUtilization = new CpuUtilization();
298 Double targetUtilization = getChildNodeDouble(childNode, "target-utilization");
299 if (targetUtilization != null) {
300 if (targetUtilization <= 0 || targetUtilization > 1) {
301 throw new AppEngineConfigException("target-utilization should be in range (0, 1].");
303 cpuUtilization.setTargetUtilization(targetUtilization);
306 cpuUtilization.setAggregationWindowLengthSec(
307 getChildNodePositiveInteger(childNode, "aggregation-window-length-sec"));
308 if (!cpuUtilization.isEmpty()) {
309 automaticScaling.setCpuUtilization(cpuUtilization);
314 private void processManualScalingNode(XmlParser.Node settingsNode,
315 AppEngineWebXml appEngineWebXml) {
316 ManualScaling manualScaling = appEngineWebXml.getManualScaling();
317 manualScaling.setInstances(getChildNodeText(settingsNode, "instances"));
320 private void processBasicScalingNode(XmlParser.Node settingsNode,
321 AppEngineWebXml appEngineWebXml) {
322 BasicScaling basicScaling = appEngineWebXml.getBasicScaling();
323 basicScaling.setMaxInstances(getChildNodeText(settingsNode, "max-instances"));
324 basicScaling.setIdleTimeout(getChildNodeText(settingsNode, "idle-timeout"));
327 private void processSslEnabledNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
328 appEngineWebXml.setSslEnabled(getBooleanValue(node));
331 private void processSessionsEnabledNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
332 appEngineWebXml.setSessionsEnabled(getBooleanValue(node));
335 private void processAsyncSessionPersistenceNode(
336 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
337 boolean enabled = getBooleanAttributeValue(node, "enabled");
338 appEngineWebXml.setAsyncSessionPersistence(enabled);
339 String queueName = trim(node.getAttribute("queue-name"));
340 appEngineWebXml.setAsyncSessionPersistenceQueueName(queueName);
343 private void processPrecompilationEnabledNode(XmlParser.Node node,
344 AppEngineWebXml appEngineWebXml) {
345 appEngineWebXml.setPrecompilationEnabled(getBooleanValue(node));
348 private void processWarmupRequestsEnabledNode(XmlParser.Node node,
349 AppEngineWebXml appEngineWebXml) {
350 appEngineWebXml.setWarmupRequestsEnabled(getBooleanValue(node));
353 private void processThreadsafeNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
354 appEngineWebXml.setThreadsafe(getBooleanValue(node));
357 private void processAutoIdPolicyNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
358 appEngineWebXml.setAutoIdPolicy(getTextNode(node));
361 private void processCodeLockNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
362 appEngineWebXml.setCodeLock(getBooleanValue(node));
365 private void processVmNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
366 appEngineWebXml.setUseVm(getBooleanValue(node));
369 private void processEnvNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
370 appEngineWebXml.setEnv(getTextNode(node));
373 private void processFilesetNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml,
374 FileType type) {
375 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "include");
376 while (nodeIter.hasNext()) {
377 XmlParser.Node includeNode = nodeIter.next();
378 String path = trim(includeNode.getAttribute("path"));
379 if (type == FileType.STATIC) {
380 String expiration = trim(includeNode.getAttribute("expiration"));
381 AppEngineWebXml.StaticFileInclude staticFileInclude =
382 appEngineWebXml.includeStaticPattern(path, expiration);
384 Map<String, String> httpHeaders = staticFileInclude.getHttpHeaders();
385 Iterator<XmlParser.Node> httpHeaderIter = getNodeIterator(includeNode, "http-header");
386 while (httpHeaderIter.hasNext()) {
387 XmlParser.Node httpHeaderNode = httpHeaderIter.next();
388 String name = httpHeaderNode.getAttribute("name");
389 String value = httpHeaderNode.getAttribute("value");
391 if (httpHeaders.containsKey(name)) {
392 throw new AppEngineConfigException("Two http-header elements have the same name.");
395 httpHeaders.put(name, value);
397 } else {
398 appEngineWebXml.includeResourcePattern(path);
402 nodeIter = getNodeIterator(node, "exclude");
403 while (nodeIter.hasNext()) {
404 XmlParser.Node subNode = nodeIter.next();
405 String path = trim(subNode.getAttribute("path"));
406 if (type == FileType.STATIC) {
407 appEngineWebXml.excludeStaticPattern(path);
408 } else {
409 appEngineWebXml.excludeResourcePattern(path);
414 private Iterator<XmlParser.Node> getNodeIterator(XmlParser.Node node, String filter) {
415 @SuppressWarnings("unchecked")
416 Iterator<XmlParser.Node> iterator = node.iterator(filter);
417 return iterator;
420 private void processSystemPropertiesNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
421 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "property");
422 while (nodeIter.hasNext()) {
423 XmlParser.Node subNode = nodeIter.next();
424 String propertyName = trim(subNode.getAttribute("name"));
425 String propertyValue = trim(subNode.getAttribute("value"));
426 appEngineWebXml.addSystemProperty(propertyName, propertyValue);
430 private void processBetaSettingsNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
431 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "setting");
432 while (nodeIter.hasNext()) {
433 XmlParser.Node subNode = nodeIter.next();
434 String name = trim(subNode.getAttribute("name"));
435 String value = trim(subNode.getAttribute("value"));
436 appEngineWebXml.addBetaSetting(name, value);
440 private void processHealthCheckNode(XmlParser.Node settingsNode,
441 AppEngineWebXml appEngineWebXml) {
442 HealthCheck healthCheck = appEngineWebXml.getHealthCheck();
443 String enableHealthCheck = trim(getChildNodeText(settingsNode, "enable-health-check"));
444 if (enableHealthCheck != null && !enableHealthCheck.isEmpty()) {
445 healthCheck.setEnableHealthCheck(toBoolean(enableHealthCheck));
447 healthCheck.setCheckIntervalSec(
448 getChildNodePositiveInteger(settingsNode, "check-interval-sec"));
449 healthCheck.setTimeoutSec(getChildNodePositiveInteger(settingsNode, "timeout-sec"));
450 healthCheck.setUnhealthyThreshold(
451 getChildNodePositiveInteger(settingsNode, "unhealthy-threshold"));
452 healthCheck.setHealthyThreshold(
453 getChildNodePositiveInteger(settingsNode, "healthy-threshold"));
454 healthCheck.setRestartThreshold(
455 getChildNodePositiveInteger(settingsNode, "restart-threshold"));
456 healthCheck.setHost(getChildNodeText(settingsNode, "host"));
459 private void processResourcesNode(XmlParser.Node settingsNode,
460 AppEngineWebXml appEngineWebXml) {
461 Resources resources = appEngineWebXml.getResources();
462 Double cpu = getChildNodeDouble(settingsNode, "cpu");
463 if (cpu != null) {
464 resources.setCpu(cpu);
466 Double memory_gb = getChildNodeDouble(settingsNode, "memory-gb");
467 if (memory_gb != null) {
468 resources.setMemoryGb(memory_gb);
470 Integer disk_size_gb = getChildNodePositiveInteger(settingsNode, "disk-size-gb");
471 if (disk_size_gb != null) {
472 resources.setDiskSizeGb(disk_size_gb);
476 private void processNetworkNode(XmlParser.Node settingsNode,
477 AppEngineWebXml appEngineWebXml) {
478 Network network = appEngineWebXml.getNetwork();
479 String instance_tag = trim(getChildNodeText(settingsNode, "instance-tag"));
480 if (instance_tag != null && !instance_tag.isEmpty()) {
481 network.setInstanceTag(instance_tag);
483 Iterator<XmlParser.Node> nodeIter = getNodeIterator(settingsNode, "forwarded-port");
484 while (nodeIter.hasNext()) {
485 XmlParser.Node subNode = nodeIter.next();
486 String forwardedPort = getTextNode(subNode);
487 network.addForwardedPort(forwardedPort);
489 String name = trim(getChildNodeText(settingsNode, "name"));
490 if (name != null && !name.isEmpty()) {
491 network.setName(name);
495 private void processEnvironmentVariablesNode(
496 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
497 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "env-var");
498 while (nodeIter.hasNext()) {
499 XmlParser.Node subNode = nodeIter.next();
500 String propertyName = trim(subNode.getAttribute("name"));
501 String propertyValue = trim(subNode.getAttribute("value"));
502 appEngineWebXml.addEnvironmentVariable(propertyName, propertyValue);
506 private void processPermissionsNode(
507 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
508 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "permission");
509 while (nodeIter.hasNext()) {
510 XmlParser.Node subNode = nodeIter.next();
511 String className = trim(subNode.getAttribute("class"));
512 String name = trim(subNode.getAttribute("name"));
513 String actions = trim(subNode.getAttribute("actions"));
514 appEngineWebXml.addUserPermission(className, name, actions);
518 private void processInboundServicesNode(
519 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
520 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "service");
521 while (nodeIter.hasNext()) {
522 XmlParser.Node subNode = nodeIter.next();
523 String service = getTextNode(subNode);
524 appEngineWebXml.addInboundService(service);
528 private void processAdminConsoleNode(
529 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
530 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "page");
531 while (nodeIter.hasNext()) {
532 XmlParser.Node subNode = nodeIter.next();
533 String name = trim(subNode.getAttribute("name"));
534 String url = trim(subNode.getAttribute("url"));
535 appEngineWebXml.addAdminConsolePage(new AdminConsolePage(name, url));
539 private void processErrorHandlerNode(
540 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
541 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "handler");
542 while (nodeIter.hasNext()) {
543 XmlParser.Node subNode = nodeIter.next();
544 String file = trim(subNode.getAttribute("file"));
545 String errorCode = trim(subNode.getAttribute("error-code"));
546 appEngineWebXml.addErrorHandler(new ErrorHandler(file, errorCode));
550 private void processApiConfigNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
551 String servlet = trim(node.getAttribute("servlet-class"));
552 String url = trim(node.getAttribute("url-pattern"));
553 appEngineWebXml.setApiConfig(new ApiConfig(servlet, url));
555 String id = null;
556 Iterator<XmlParser.Node> subNodeIter = getNodeIterator(node, "endpoint-servlet-mapping-id");
557 while (subNodeIter.hasNext()) {
558 XmlParser.Node subNode = subNodeIter.next();
559 id = trim(getTextNode(subNode));
560 if (id != null && id.length() > 0) {
561 appEngineWebXml.addApiEndpoint(id);
566 private void processPagespeedNode(XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
567 Pagespeed pagespeed = new Pagespeed();
568 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "url-blacklist");
569 while (nodeIter.hasNext()) {
570 XmlParser.Node subNode = nodeIter.next();
571 String urlMatcher = getTextNode(subNode);
572 pagespeed.addUrlBlacklist(urlMatcher);
574 nodeIter = getNodeIterator(node, "domain-to-rewrite");
575 while (nodeIter.hasNext()) {
576 XmlParser.Node subNode = nodeIter.next();
577 String domain = getTextNode(subNode);
578 pagespeed.addDomainToRewrite(domain);
580 nodeIter = getNodeIterator(node, "enabled-rewriter");
581 while (nodeIter.hasNext()) {
582 XmlParser.Node subNode = nodeIter.next();
583 String rewriter = getTextNode(subNode);
584 pagespeed.addEnabledRewriter(rewriter);
586 nodeIter = getNodeIterator(node, "disabled-rewriter");
587 while (nodeIter.hasNext()) {
588 XmlParser.Node subNode = nodeIter.next();
589 String rewriter = getTextNode(subNode);
590 pagespeed.addDisabledRewriter(rewriter);
592 appEngineWebXml.setPagespeed(pagespeed);
595 private void processClassLoaderConfig(
596 XmlParser.Node node, AppEngineWebXml appEngineWebXml) {
597 ClassLoaderConfig config = new ClassLoaderConfig();
598 appEngineWebXml.setClassLoaderConfig(config);
599 Iterator<XmlParser.Node> nodeIter = getNodeIterator(node, "priority-specifier");
600 while (nodeIter.hasNext()) {
601 processClassPathPrioritySpecifier(nodeIter.next(), config);
605 private void processClassPathPrioritySpecifier(Node node, ClassLoaderConfig config) {
606 PrioritySpecifierEntry entry = new PrioritySpecifierEntry();
607 entry.setFilename(node.getAttribute("filename"));
608 entry.setPriority(node.getAttribute("priority"));
609 entry.checkClassLoaderConfig();
610 config.add(entry);
613 private void processUrlStreamHandler(Node node, AppEngineWebXml appEngineWebXml) {
614 appEngineWebXml.setUrlStreamHandlerType(getTextNode(node));
617 private boolean getBooleanValue(XmlParser.Node node) {
618 return toBoolean(getTextNode(node));
621 private boolean getBooleanAttributeValue(XmlParser.Node node, String attribute) {
622 return toBoolean(node.getAttribute(attribute));
625 private boolean toBoolean(String value) {
626 value = value.trim();
627 return (value.equalsIgnoreCase("true") || value.equals("1"));
630 private String getTextNode(XmlParser.Node node) {
631 String value = (String) node.get(0);
632 if (value == null) {
633 value = "";
635 return value.trim();
638 private String trim(String attribute) {
639 return attribute == null ? null : attribute.trim();
642 private void processUseGoogleConnectorJNode(XmlParser.Node node,
643 AppEngineWebXml appEngineWebXml) {
644 appEngineWebXml.setUseGoogleConnectorJ(getBooleanValue(node));