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
;
26 import java
.util
.logging
.Level
;
27 import java
.util
.logging
.Logger
;
30 * Constructs an {@link AppEngineWebXml} from an xml document corresponding to
31 * appengine-web.xsd. We use Jetty's {@link XmlParser} utility for
34 * @TODO(user): Add a real link to the xsd once it exists and do schema
38 class AppEngineWebXmlProcessor
{
40 enum FileType
{ STATIC
, RESOURCE
}
42 private static final Logger logger
= Logger
.getLogger(AppEngineWebXmlProcessor
.class.getName());
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
)) {
61 XmlParser
.Node node
= (XmlParser
.Node
) o
;
62 processSecondLevelNode(node
, appEngineWebXml
);
64 checkScalingConstraints(appEngineWebXml
);
65 return appEngineWebXml
;
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;
78 throw new AppEngineConfigException(
79 "There may be only one of 'automatic-scaling', 'manual-scaling' or " +
80 "'basic-scaling' elements.");
85 * Given an InputStream, create a Node corresponding to the top level xml
88 * @throws AppEngineConfigException If the input stream cannot be parsed.
90 XmlParser
.Node
getTopLevelNode(InputStream is
) {
91 XmlParser xmlParser
= new XmlParser();
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("api-config")) {
168 processApiConfigNode(node
, appEngineWebXml
);
169 } else if (elementName
.equals("pagespeed")) {
170 processPagespeedNode(node
, appEngineWebXml
);
171 } else if (elementName
.equals("class-loader-config")) {
172 processClassLoaderConfig(node
, appEngineWebXml
);
173 } else if (elementName
.equals("url-stream-handler")) {
174 processUrlStreamHandler(node
, appEngineWebXml
);
175 } else if (elementName
.equals("use-google-connector-j")) {
176 processUseGoogleConnectorJNode(node
, appEngineWebXml
);
178 throw new AppEngineConfigException("Unrecognized element <" + elementName
+ ">");
182 private void processApplicationNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
183 appEngineWebXml
.setAppId(getTextNode(node
));
186 private void processPublicRootNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
187 appEngineWebXml
.setPublicRoot(getTextNode(node
));
190 private void processVersionNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
191 appEngineWebXml
.setMajorVersionId(getTextNode(node
));
194 private void processSourceLanguageNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
195 appEngineWebXml
.setSourceLanguage(getTextNode(node
));
198 private void processModuleNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
199 appEngineWebXml
.setModule(getTextNode(node
));
202 private void processInstanceClassNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
203 appEngineWebXml
.setInstanceClass(getTextNode(node
));
206 private String
getChildNodeText(XmlParser
.Node parentNode
, String childTag
) {
207 String result
= null;
208 XmlParser
.Node node
= parentNode
.get(childTag
);
210 result
= (String
) node
.get(0);
215 private Integer
getChildNodePositiveInteger(XmlParser
.Node parentNode
, String childTag
) {
216 Integer result
= null;
217 XmlParser
.Node node
= parentNode
.get(childTag
);
218 if (node
!= null && node
.get(0) != null) {
219 String trimmedText
= ((String
) node
.get(0)).trim();
220 if (!trimmedText
.isEmpty()) {
222 result
= Integer
.parseInt(trimmedText
);
223 } catch (NumberFormatException ex
) {
224 throw new AppEngineConfigException(childTag
+ " should only contain integers.");
228 throw new AppEngineConfigException(childTag
+ " should only contain positive integers.");
235 private Double
getChildNodeDouble(XmlParser
.Node parentNode
, String childTag
) {
236 Double result
= null;
237 XmlParser
.Node node
= parentNode
.get(childTag
);
238 if (node
!= null && node
.get(0) != null) {
239 String trimmedText
= ((String
) node
.get(0)).trim();
240 if (!trimmedText
.isEmpty()) {
242 result
= Double
.parseDouble(trimmedText
);
243 } catch (NumberFormatException ex
) {
244 throw new AppEngineConfigException(childTag
+ " should only contain doubles.");
245 } catch (NullPointerException ex
) {
246 throw new AppEngineConfigException(childTag
+ " should NOT be empty.");
253 private void processAutomaticScalingNode(XmlParser
.Node settingsNode
,
254 AppEngineWebXml appEngineWebXml
) {
255 AutomaticScaling automaticScaling
= appEngineWebXml
.getAutomaticScaling();
256 automaticScaling
.setMinPendingLatency(getChildNodeText(settingsNode
, "min-pending-latency"));
257 automaticScaling
.setMaxPendingLatency(getChildNodeText(settingsNode
, "max-pending-latency"));
258 automaticScaling
.setMinIdleInstances(getChildNodeText(settingsNode
, "min-idle-instances"));
259 automaticScaling
.setMaxIdleInstances(getChildNodeText(settingsNode
, "max-idle-instances"));
260 automaticScaling
.setMaxConcurrentRequests(
261 getChildNodeText(settingsNode
, "max-concurrent-requests"));
262 automaticScaling
.setMinNumInstances(
263 getChildNodePositiveInteger(settingsNode
, "min-num-instances"));
264 automaticScaling
.setMaxNumInstances(
265 getChildNodePositiveInteger(settingsNode
, "max-num-instances"));
266 automaticScaling
.setCoolDownPeriodSec(
267 getChildNodePositiveInteger(settingsNode
, "cool-down-period-sec"));
268 processCpuUtilizationNode(settingsNode
, automaticScaling
);
271 private void processCpuUtilizationNode(
272 XmlParser
.Node settingsNode
, AutomaticScaling automaticScaling
) {
273 XmlParser
.Node childNode
= settingsNode
.get("cpu-utilization");
274 if (childNode
!= null) {
275 CpuUtilization cpuUtilization
= new CpuUtilization();
276 Double targetUtilization
= getChildNodeDouble(childNode
, "target-utilization");
277 if (targetUtilization
!= null) {
278 if (targetUtilization
<= 0 || targetUtilization
> 1) {
279 throw new AppEngineConfigException("target-utilization should be in range (0, 1].");
281 cpuUtilization
.setTargetUtilization(targetUtilization
);
284 cpuUtilization
.setAggregationWindowLengthSec(
285 getChildNodePositiveInteger(childNode
, "aggregation-window-length-sec"));
286 if (!cpuUtilization
.isEmpty()) {
287 automaticScaling
.setCpuUtilization(cpuUtilization
);
292 private void processManualScalingNode(XmlParser
.Node settingsNode
,
293 AppEngineWebXml appEngineWebXml
) {
294 ManualScaling manualScaling
= appEngineWebXml
.getManualScaling();
295 manualScaling
.setInstances(getChildNodeText(settingsNode
, "instances"));
298 private void processBasicScalingNode(XmlParser
.Node settingsNode
,
299 AppEngineWebXml appEngineWebXml
) {
300 BasicScaling basicScaling
= appEngineWebXml
.getBasicScaling();
301 basicScaling
.setMaxInstances(getChildNodeText(settingsNode
, "max-instances"));
302 basicScaling
.setIdleTimeout(getChildNodeText(settingsNode
, "idle-timeout"));
305 private void processSslEnabledNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
306 appEngineWebXml
.setSslEnabled(getBooleanValue(node
));
309 private void processSessionsEnabledNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
310 appEngineWebXml
.setSessionsEnabled(getBooleanValue(node
));
313 private void processAsyncSessionPersistenceNode(
314 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
315 boolean enabled
= getBooleanAttributeValue(node
, "enabled");
316 appEngineWebXml
.setAsyncSessionPersistence(enabled
);
317 String queueName
= trim(node
.getAttribute("queue-name"));
318 appEngineWebXml
.setAsyncSessionPersistenceQueueName(queueName
);
321 private void processPrecompilationEnabledNode(XmlParser
.Node node
,
322 AppEngineWebXml appEngineWebXml
) {
323 appEngineWebXml
.setPrecompilationEnabled(getBooleanValue(node
));
326 private void processWarmupRequestsEnabledNode(XmlParser
.Node node
,
327 AppEngineWebXml appEngineWebXml
) {
328 appEngineWebXml
.setWarmupRequestsEnabled(getBooleanValue(node
));
331 private void processThreadsafeNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
332 appEngineWebXml
.setThreadsafe(getBooleanValue(node
));
335 private void processAutoIdPolicyNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
336 appEngineWebXml
.setAutoIdPolicy(getTextNode(node
));
339 private void processCodeLockNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
340 appEngineWebXml
.setCodeLock(getBooleanValue(node
));
343 private void processVmNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
344 appEngineWebXml
.setUseVm(getBooleanValue(node
));
347 private void processFilesetNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
,
349 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "include");
350 while (nodeIter
.hasNext()) {
351 XmlParser
.Node includeNode
= nodeIter
.next();
352 String path
= trim(includeNode
.getAttribute("path"));
353 if (type
== FileType
.STATIC
) {
354 String expiration
= trim(includeNode
.getAttribute("expiration"));
355 AppEngineWebXml
.StaticFileInclude staticFileInclude
=
356 appEngineWebXml
.includeStaticPattern(path
, expiration
);
358 Map
<String
, String
> httpHeaders
= staticFileInclude
.getHttpHeaders();
359 Iterator
<XmlParser
.Node
> httpHeaderIter
= getNodeIterator(includeNode
, "http-header");
360 while (httpHeaderIter
.hasNext()) {
361 XmlParser
.Node httpHeaderNode
= httpHeaderIter
.next();
362 String name
= httpHeaderNode
.getAttribute("name");
363 String value
= httpHeaderNode
.getAttribute("value");
365 if (httpHeaders
.containsKey(name
)) {
366 throw new AppEngineConfigException("Two http-header elements have the same name.");
369 httpHeaders
.put(name
, value
);
372 appEngineWebXml
.includeResourcePattern(path
);
376 nodeIter
= getNodeIterator(node
, "exclude");
377 while (nodeIter
.hasNext()) {
378 XmlParser
.Node subNode
= nodeIter
.next();
379 String path
= trim(subNode
.getAttribute("path"));
380 if (type
== FileType
.STATIC
) {
381 appEngineWebXml
.excludeStaticPattern(path
);
383 appEngineWebXml
.excludeResourcePattern(path
);
388 private Iterator
<XmlParser
.Node
> getNodeIterator(XmlParser
.Node node
, String filter
) {
389 @SuppressWarnings("unchecked")
390 Iterator
<XmlParser
.Node
> iterator
= node
.iterator(filter
);
394 private void processSystemPropertiesNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
395 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "property");
396 while (nodeIter
.hasNext()) {
397 XmlParser
.Node subNode
= nodeIter
.next();
398 String propertyName
= trim(subNode
.getAttribute("name"));
399 String propertyValue
= trim(subNode
.getAttribute("value"));
400 appEngineWebXml
.addSystemProperty(propertyName
, propertyValue
);
404 private void processBetaSettingsNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
405 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "setting");
406 while (nodeIter
.hasNext()) {
407 XmlParser
.Node subNode
= nodeIter
.next();
408 String name
= trim(subNode
.getAttribute("name"));
409 String value
= trim(subNode
.getAttribute("value"));
410 appEngineWebXml
.addBetaSetting(name
, value
);
414 private void processHealthCheckNode(XmlParser
.Node settingsNode
,
415 AppEngineWebXml appEngineWebXml
) {
416 HealthCheck healthCheck
= appEngineWebXml
.getHealthCheck();
417 String enableHealthCheck
= trim(getChildNodeText(settingsNode
, "enable-health-check"));
418 if (enableHealthCheck
!= null && !enableHealthCheck
.isEmpty()) {
419 healthCheck
.setEnableHealthCheck(toBoolean(enableHealthCheck
));
421 healthCheck
.setCheckIntervalSec(
422 getChildNodePositiveInteger(settingsNode
, "check-interval-sec"));
423 healthCheck
.setTimeoutSec(getChildNodePositiveInteger(settingsNode
, "timeout-sec"));
424 healthCheck
.setUnhealthyThreshold(
425 getChildNodePositiveInteger(settingsNode
, "unhealthy-threshold"));
426 healthCheck
.setHealthyThreshold(
427 getChildNodePositiveInteger(settingsNode
, "healthy-threshold"));
428 healthCheck
.setRestartThreshold(
429 getChildNodePositiveInteger(settingsNode
, "restart-threshold"));
430 healthCheck
.setHost(getChildNodeText(settingsNode
, "host"));
433 private void processResourcesNode(XmlParser
.Node settingsNode
,
434 AppEngineWebXml appEngineWebXml
) {
435 Resources resources
= appEngineWebXml
.getResources();
436 Double cpu
= getChildNodeDouble(settingsNode
, "cpu");
438 resources
.setCpu(cpu
);
440 Double memory_gb
= getChildNodeDouble(settingsNode
, "memory-gb");
441 if (memory_gb
!= null) {
442 resources
.setMemoryGb(memory_gb
);
444 Integer disk_size_gb
= getChildNodePositiveInteger(settingsNode
, "disk-size-gb");
445 if (disk_size_gb
!= null) {
446 resources
.setDiskSizeGb(disk_size_gb
);
450 private void processNetworkNode(XmlParser
.Node settingsNode
,
451 AppEngineWebXml appEngineWebXml
) {
452 Network network
= appEngineWebXml
.getNetwork();
453 String instance_tag
= trim(getChildNodeText(settingsNode
, "instance-tag"));
454 if (instance_tag
!= null && !instance_tag
.isEmpty()) {
455 network
.setInstanceTag(instance_tag
);
457 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(settingsNode
, "forwarded-port");
458 while (nodeIter
.hasNext()) {
459 XmlParser
.Node subNode
= nodeIter
.next();
460 String forwardedPort
= getTextNode(subNode
);
461 network
.addForwardedPort(forwardedPort
);
465 private void processEnvironmentVariablesNode(
466 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
467 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "env-var");
468 while (nodeIter
.hasNext()) {
469 XmlParser
.Node subNode
= nodeIter
.next();
470 String propertyName
= trim(subNode
.getAttribute("name"));
471 String propertyValue
= trim(subNode
.getAttribute("value"));
472 appEngineWebXml
.addEnvironmentVariable(propertyName
, propertyValue
);
476 private void processPermissionsNode(
477 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
478 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "permission");
479 while (nodeIter
.hasNext()) {
480 XmlParser
.Node subNode
= nodeIter
.next();
481 String className
= trim(subNode
.getAttribute("class"));
482 String name
= trim(subNode
.getAttribute("name"));
483 String actions
= trim(subNode
.getAttribute("actions"));
484 appEngineWebXml
.addUserPermission(className
, name
, actions
);
488 private void processInboundServicesNode(
489 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
490 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "service");
491 while (nodeIter
.hasNext()) {
492 XmlParser
.Node subNode
= nodeIter
.next();
493 String service
= getTextNode(subNode
);
494 appEngineWebXml
.addInboundService(service
);
498 private void processAdminConsoleNode(
499 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
500 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "page");
501 while (nodeIter
.hasNext()) {
502 XmlParser
.Node subNode
= nodeIter
.next();
503 String name
= trim(subNode
.getAttribute("name"));
504 String url
= trim(subNode
.getAttribute("url"));
505 appEngineWebXml
.addAdminConsolePage(new AdminConsolePage(name
, url
));
509 private void processErrorHandlerNode(
510 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
511 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "handler");
512 while (nodeIter
.hasNext()) {
513 XmlParser
.Node subNode
= nodeIter
.next();
514 String file
= trim(subNode
.getAttribute("file"));
515 String errorCode
= trim(subNode
.getAttribute("error-code"));
516 appEngineWebXml
.addErrorHandler(new ErrorHandler(file
, errorCode
));
520 private void processApiConfigNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
521 String servlet
= trim(node
.getAttribute("servlet-class"));
522 String url
= trim(node
.getAttribute("url-pattern"));
523 appEngineWebXml
.setApiConfig(new ApiConfig(servlet
, url
));
526 Iterator
<XmlParser
.Node
> subNodeIter
= getNodeIterator(node
, "endpoint-servlet-mapping-id");
527 while (subNodeIter
.hasNext()) {
528 XmlParser
.Node subNode
= subNodeIter
.next();
529 id
= trim(getTextNode(subNode
));
530 if (id
!= null && id
.length() > 0) {
531 appEngineWebXml
.addApiEndpoint(id
);
536 private void processPagespeedNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
537 Pagespeed pagespeed
= new Pagespeed();
538 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "url-blacklist");
539 while (nodeIter
.hasNext()) {
540 XmlParser
.Node subNode
= nodeIter
.next();
541 String urlMatcher
= getTextNode(subNode
);
542 pagespeed
.addUrlBlacklist(urlMatcher
);
544 nodeIter
= getNodeIterator(node
, "domain-to-rewrite");
545 while (nodeIter
.hasNext()) {
546 XmlParser
.Node subNode
= nodeIter
.next();
547 String domain
= getTextNode(subNode
);
548 pagespeed
.addDomainToRewrite(domain
);
550 nodeIter
= getNodeIterator(node
, "enabled-rewriter");
551 while (nodeIter
.hasNext()) {
552 XmlParser
.Node subNode
= nodeIter
.next();
553 String rewriter
= getTextNode(subNode
);
554 pagespeed
.addEnabledRewriter(rewriter
);
556 nodeIter
= getNodeIterator(node
, "disabled-rewriter");
557 while (nodeIter
.hasNext()) {
558 XmlParser
.Node subNode
= nodeIter
.next();
559 String rewriter
= getTextNode(subNode
);
560 pagespeed
.addDisabledRewriter(rewriter
);
562 appEngineWebXml
.setPagespeed(pagespeed
);
565 private void processClassLoaderConfig(
566 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
567 ClassLoaderConfig config
= new ClassLoaderConfig();
568 appEngineWebXml
.setClassLoaderConfig(config
);
569 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "priority-specifier");
570 while (nodeIter
.hasNext()) {
571 processClassPathPrioritySpecifier(nodeIter
.next(), config
);
575 private void processClassPathPrioritySpecifier(Node node
, ClassLoaderConfig config
) {
576 PrioritySpecifierEntry entry
= new PrioritySpecifierEntry();
577 entry
.setFilename(node
.getAttribute("filename"));
578 entry
.setPriority(node
.getAttribute("priority"));
579 entry
.checkClassLoaderConfig();
583 private void processUrlStreamHandler(Node node
, AppEngineWebXml appEngineWebXml
) {
584 appEngineWebXml
.setUrlStreamHandlerType(getTextNode(node
));
587 private boolean getBooleanValue(XmlParser
.Node node
) {
588 return toBoolean(getTextNode(node
));
591 private boolean getBooleanAttributeValue(XmlParser
.Node node
, String attribute
) {
592 return toBoolean(node
.getAttribute(attribute
));
595 private boolean toBoolean(String value
) {
596 value
= value
.trim();
597 return (value
.equalsIgnoreCase("true") || value
.equals("1"));
600 private String
getTextNode(XmlParser
.Node node
) {
601 String value
= (String
) node
.get(0);
608 private String
trim(String attribute
) {
609 return attribute
== null ?
null : attribute
.trim();
612 private void processUseGoogleConnectorJNode(XmlParser
.Node node
,
613 AppEngineWebXml appEngineWebXml
) {
614 appEngineWebXml
.setUseGoogleConnectorJ(getBooleanValue(node
));