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());
43 private boolean moduleNodeFound
;
44 private boolean serviceNodeFound
;
47 * Construct an {@link AppEngineWebXml} from the xml document
48 * identified by the provided {@link InputStream}.
50 * @param is The InputStream containing the xml we want to parse and process.
52 * @return Object representation of the xml document.
53 * @throws AppEngineConfigException If the input stream cannot be parsed.
55 public AppEngineWebXml
processXml(InputStream is
) {
56 XmlParser
.Node config
= getTopLevelNode(is
);
57 AppEngineWebXml appEngineWebXml
= new AppEngineWebXml();
58 appEngineWebXml
.setWarmupRequestsEnabled(true);
59 for (Object o
: config
) {
60 if (!(o
instanceof XmlParser
.Node
)) {
63 XmlParser
.Node node
= (XmlParser
.Node
) o
;
64 processSecondLevelNode(node
, appEngineWebXml
);
66 checkScalingConstraints(appEngineWebXml
);
67 if (serviceNodeFound
&& moduleNodeFound
) {
68 throw new AppEngineConfigException(
69 "The <service> and <module> elements are conflicting. "
70 + "Please remove the deprecated <module> element.");
72 return appEngineWebXml
;
76 * Given an AppEngineWebXml, ensure it has no more than one of the scaling options available.
78 * @throws AppEngineConfigException If there is more than one scaling option selected.
80 private static void checkScalingConstraints(AppEngineWebXml appEngineWebXml
) {
81 int count
= appEngineWebXml
.getManualScaling().isEmpty() ?
0 : 1;
82 count
+= appEngineWebXml
.getBasicScaling().isEmpty() ?
0 : 1;
83 count
+= appEngineWebXml
.getAutomaticScaling().isEmpty() ?
0 : 1;
85 throw new AppEngineConfigException(
86 "There may be only one of 'automatic-scaling', 'manual-scaling' or " +
87 "'basic-scaling' elements.");
92 * Given an InputStream, create a Node corresponding to the top level xml
95 * @throws AppEngineConfigException If the input stream cannot be parsed.
97 XmlParser
.Node
getTopLevelNode(InputStream is
) {
98 XmlParser xmlParser
= new XmlParser();
100 return xmlParser
.parse(is
);
101 } catch (IOException e
) {
102 String msg
= "Received IOException parsing the input stream.";
103 logger
.log(Level
.SEVERE
, msg
, e
);
104 throw new AppEngineConfigException(msg
, e
);
105 } catch (SAXException e
) {
106 String msg
= "Received SAXException parsing the input stream.";
107 logger
.log(Level
.SEVERE
, msg
, e
);
108 throw new AppEngineConfigException(msg
, e
);
112 private void processSecondLevelNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
113 String elementName
= node
.getTag();
114 if (elementName
.equals("system-properties")) {
115 processSystemPropertiesNode(node
, appEngineWebXml
);
116 } else if (elementName
.equals("vm-settings") || elementName
.equals("beta-settings")) {
117 processBetaSettingsNode(node
, appEngineWebXml
);
118 } else if (elementName
.equals("vm-health-check") || elementName
.equals("health-check")) {
119 processHealthCheckNode(node
, appEngineWebXml
);
120 } else if (elementName
.equals("resources")) {
121 processResourcesNode(node
, appEngineWebXml
);
122 } else if (elementName
.equals("network")) {
123 processNetworkNode(node
, appEngineWebXml
);
124 } else if (elementName
.equals("env-variables")) {
125 processEnvironmentVariablesNode(node
, appEngineWebXml
);
126 } else if (elementName
.equals("application")) {
127 processApplicationNode(node
, appEngineWebXml
);
128 } else if (elementName
.equals("version")) {
129 processVersionNode(node
, appEngineWebXml
);
130 } else if (elementName
.equals("source-language")) {
131 processSourceLanguageNode(node
, appEngineWebXml
);
132 } else if (elementName
.equals("module")) {
133 moduleNodeFound
= true;
134 processModuleNode(node
, appEngineWebXml
);
135 } else if (elementName
.equals("service")) {
136 serviceNodeFound
= true;
137 processServiceNode(node
, appEngineWebXml
);
138 } else if (elementName
.equals("instance-class")) {
139 processInstanceClassNode(node
, appEngineWebXml
);
140 } else if (elementName
.equals("automatic-scaling")) {
141 processAutomaticScalingNode(node
, appEngineWebXml
);
142 } else if (elementName
.equals("manual-scaling")) {
143 processManualScalingNode(node
, appEngineWebXml
);
144 } else if (elementName
.equals("basic-scaling")) {
145 processBasicScalingNode(node
, appEngineWebXml
);
146 } else if (elementName
.equals("static-files")) {
147 processFilesetNode(node
, appEngineWebXml
, FileType
.STATIC
);
148 } else if (elementName
.equals("resource-files")) {
149 processFilesetNode(node
, appEngineWebXml
, FileType
.RESOURCE
);
150 } else if (elementName
.equals("ssl-enabled")) {
151 processSslEnabledNode(node
, appEngineWebXml
);
152 } else if (elementName
.equals("sessions-enabled")) {
153 processSessionsEnabledNode(node
, appEngineWebXml
);
154 } else if (elementName
.equals("async-session-persistence")) {
155 processAsyncSessionPersistenceNode(node
, appEngineWebXml
);
156 } else if (elementName
.equals("user-permissions")) {
157 processPermissionsNode(node
, appEngineWebXml
);
158 } else if (elementName
.equals("public-root")) {
159 processPublicRootNode(node
, appEngineWebXml
);
160 } else if (elementName
.equals("inbound-services")) {
161 processInboundServicesNode(node
, appEngineWebXml
);
162 } else if (elementName
.equals("precompilation-enabled")) {
163 processPrecompilationEnabledNode(node
, appEngineWebXml
);
164 } else if (elementName
.equals("admin-console")) {
165 processAdminConsoleNode(node
, appEngineWebXml
);
166 } else if (elementName
.equals("static-error-handlers")) {
167 processErrorHandlerNode(node
, appEngineWebXml
);
168 } else if (elementName
.equals("warmup-requests-enabled")) {
169 processWarmupRequestsEnabledNode(node
, appEngineWebXml
);
170 } else if (elementName
.equals("threadsafe")) {
171 processThreadsafeNode(node
, appEngineWebXml
);
172 } else if (elementName
.equals("auto-id-policy")) {
173 processAutoIdPolicyNode(node
, appEngineWebXml
);
174 } else if (elementName
.equals("code-lock")) {
175 processCodeLockNode(node
, appEngineWebXml
);
176 } else if (elementName
.equals("vm")) {
177 processVmNode(node
, appEngineWebXml
);
178 } else if (elementName
.equals("env")) {
179 processEnvNode(node
, appEngineWebXml
);
180 } else if (elementName
.equals("api-config")) {
181 processApiConfigNode(node
, appEngineWebXml
);
182 } else if (elementName
.equals("pagespeed")) {
183 processPagespeedNode(node
, appEngineWebXml
);
184 } else if (elementName
.equals("class-loader-config")) {
185 processClassLoaderConfig(node
, appEngineWebXml
);
186 } else if (elementName
.equals("url-stream-handler")) {
187 processUrlStreamHandler(node
, appEngineWebXml
);
188 } else if (elementName
.equals("use-google-connector-j")) {
189 processUseGoogleConnectorJNode(node
, appEngineWebXml
);
191 throw new AppEngineConfigException("Unrecognized element <" + elementName
+ ">");
195 private void processApplicationNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
196 appEngineWebXml
.setAppId(getTextNode(node
));
199 private void processPublicRootNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
200 appEngineWebXml
.setPublicRoot(getTextNode(node
));
203 private void processVersionNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
204 appEngineWebXml
.setMajorVersionId(getTextNode(node
));
207 private void processSourceLanguageNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
208 appEngineWebXml
.setSourceLanguage(getTextNode(node
));
211 private void processModuleNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
212 appEngineWebXml
.setModule(getTextNode(node
));
215 private void processServiceNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
216 appEngineWebXml
.setService(getTextNode(node
));
219 private void processInstanceClassNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
220 appEngineWebXml
.setInstanceClass(getTextNode(node
));
223 private String
getChildNodeText(XmlParser
.Node parentNode
, String childTag
) {
224 String result
= null;
225 XmlParser
.Node node
= parentNode
.get(childTag
);
227 result
= (String
) node
.get(0);
232 private Integer
getChildNodePositiveInteger(XmlParser
.Node parentNode
, String childTag
) {
233 Integer result
= null;
234 XmlParser
.Node node
= parentNode
.get(childTag
);
235 if (node
!= null && node
.get(0) != null) {
236 String trimmedText
= ((String
) node
.get(0)).trim();
237 if (!trimmedText
.isEmpty()) {
239 result
= Integer
.parseInt(trimmedText
);
240 } catch (NumberFormatException ex
) {
241 throw new AppEngineConfigException(childTag
+ " should only contain integers.");
245 throw new AppEngineConfigException(childTag
+ " should only contain positive integers.");
252 private Double
getChildNodeDouble(XmlParser
.Node parentNode
, String childTag
) {
253 Double result
= null;
254 XmlParser
.Node node
= parentNode
.get(childTag
);
255 if (node
!= null && node
.get(0) != null) {
256 String trimmedText
= ((String
) node
.get(0)).trim();
257 if (!trimmedText
.isEmpty()) {
259 result
= Double
.parseDouble(trimmedText
);
260 } catch (NumberFormatException ex
) {
261 throw new AppEngineConfigException(childTag
+ " should only contain doubles.");
262 } catch (NullPointerException ex
) {
263 throw new AppEngineConfigException(childTag
+ " should NOT be empty.");
270 private void processAutomaticScalingNode(XmlParser
.Node settingsNode
,
271 AppEngineWebXml appEngineWebXml
) {
272 AutomaticScaling automaticScaling
= appEngineWebXml
.getAutomaticScaling();
273 automaticScaling
.setMinPendingLatency(getChildNodeText(settingsNode
, "min-pending-latency"));
274 automaticScaling
.setMaxPendingLatency(getChildNodeText(settingsNode
, "max-pending-latency"));
275 automaticScaling
.setMinIdleInstances(getChildNodeText(settingsNode
, "min-idle-instances"));
276 automaticScaling
.setMaxIdleInstances(getChildNodeText(settingsNode
, "max-idle-instances"));
277 automaticScaling
.setMaxConcurrentRequests(
278 getChildNodeText(settingsNode
, "max-concurrent-requests"));
279 automaticScaling
.setMinNumInstances(
280 getChildNodePositiveInteger(settingsNode
, "min-num-instances"));
281 automaticScaling
.setMaxNumInstances(
282 getChildNodePositiveInteger(settingsNode
, "max-num-instances"));
283 automaticScaling
.setCoolDownPeriodSec(
284 getChildNodePositiveInteger(settingsNode
, "cool-down-period-sec"));
285 processCpuUtilizationNode(settingsNode
, automaticScaling
);
286 automaticScaling
.setTargetNetworkSentBytesPerSec(
287 getChildNodePositiveInteger(settingsNode
, "target-network-sent-bytes-per-sec"));
288 automaticScaling
.setTargetNetworkSentPacketsPerSec(
289 getChildNodePositiveInteger(settingsNode
, "target-network-sent-packets-per-sec"));
290 automaticScaling
.setTargetNetworkReceivedBytesPerSec(
291 getChildNodePositiveInteger(settingsNode
, "target-network-received-bytes-per-sec"));
292 automaticScaling
.setTargetNetworkReceivedPacketsPerSec(
293 getChildNodePositiveInteger(settingsNode
, "target-network-received-packets-per-sec"));
294 automaticScaling
.setTargetDiskWriteBytesPerSec(
295 getChildNodePositiveInteger(settingsNode
, "target-disk-write-bytes-per-sec"));
296 automaticScaling
.setTargetDiskWriteOpsPerSec(
297 getChildNodePositiveInteger(settingsNode
, "target-disk-write-ops-per-sec"));
298 automaticScaling
.setTargetDiskReadBytesPerSec(
299 getChildNodePositiveInteger(settingsNode
, "target-disk-read-bytes-per-sec"));
300 automaticScaling
.setTargetDiskReadOpsPerSec(
301 getChildNodePositiveInteger(settingsNode
, "target-disk-read-ops-per-sec"));
302 automaticScaling
.setTargetRequestCountPerSec(
303 getChildNodePositiveInteger(settingsNode
, "target-request-count-per-sec"));
304 automaticScaling
.setTargetConcurrentRequests(
305 getChildNodePositiveInteger(settingsNode
, "target-concurrent-requests"));
308 private void processCpuUtilizationNode(
309 XmlParser
.Node settingsNode
, AutomaticScaling automaticScaling
) {
310 XmlParser
.Node childNode
= settingsNode
.get("cpu-utilization");
311 if (childNode
!= null) {
312 CpuUtilization cpuUtilization
= new CpuUtilization();
313 Double targetUtilization
= getChildNodeDouble(childNode
, "target-utilization");
314 if (targetUtilization
!= null) {
315 if (targetUtilization
<= 0 || targetUtilization
> 1) {
316 throw new AppEngineConfigException("target-utilization should be in range (0, 1].");
318 cpuUtilization
.setTargetUtilization(targetUtilization
);
321 cpuUtilization
.setAggregationWindowLengthSec(
322 getChildNodePositiveInteger(childNode
, "aggregation-window-length-sec"));
323 if (!cpuUtilization
.isEmpty()) {
324 automaticScaling
.setCpuUtilization(cpuUtilization
);
329 private void processManualScalingNode(XmlParser
.Node settingsNode
,
330 AppEngineWebXml appEngineWebXml
) {
331 ManualScaling manualScaling
= appEngineWebXml
.getManualScaling();
332 manualScaling
.setInstances(getChildNodeText(settingsNode
, "instances"));
335 private void processBasicScalingNode(XmlParser
.Node settingsNode
,
336 AppEngineWebXml appEngineWebXml
) {
337 BasicScaling basicScaling
= appEngineWebXml
.getBasicScaling();
338 basicScaling
.setMaxInstances(getChildNodeText(settingsNode
, "max-instances"));
339 basicScaling
.setIdleTimeout(getChildNodeText(settingsNode
, "idle-timeout"));
342 private void processSslEnabledNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
343 appEngineWebXml
.setSslEnabled(getBooleanValue(node
));
346 private void processSessionsEnabledNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
347 appEngineWebXml
.setSessionsEnabled(getBooleanValue(node
));
350 private void processAsyncSessionPersistenceNode(
351 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
352 boolean enabled
= getBooleanAttributeValue(node
, "enabled");
353 appEngineWebXml
.setAsyncSessionPersistence(enabled
);
354 String queueName
= trim(node
.getAttribute("queue-name"));
355 appEngineWebXml
.setAsyncSessionPersistenceQueueName(queueName
);
358 private void processPrecompilationEnabledNode(XmlParser
.Node node
,
359 AppEngineWebXml appEngineWebXml
) {
360 appEngineWebXml
.setPrecompilationEnabled(getBooleanValue(node
));
363 private void processWarmupRequestsEnabledNode(XmlParser
.Node node
,
364 AppEngineWebXml appEngineWebXml
) {
365 appEngineWebXml
.setWarmupRequestsEnabled(getBooleanValue(node
));
368 private void processThreadsafeNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
369 appEngineWebXml
.setThreadsafe(getBooleanValue(node
));
372 private void processAutoIdPolicyNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
373 appEngineWebXml
.setAutoIdPolicy(getTextNode(node
));
376 private void processCodeLockNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
377 appEngineWebXml
.setCodeLock(getBooleanValue(node
));
380 private void processVmNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
381 appEngineWebXml
.setUseVm(getBooleanValue(node
));
384 private void processEnvNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
385 appEngineWebXml
.setEnv(getTextNode(node
));
388 private void processFilesetNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
,
390 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "include");
391 while (nodeIter
.hasNext()) {
392 XmlParser
.Node includeNode
= nodeIter
.next();
393 String path
= trim(includeNode
.getAttribute("path"));
394 if (type
== FileType
.STATIC
) {
395 String expiration
= trim(includeNode
.getAttribute("expiration"));
396 AppEngineWebXml
.StaticFileInclude staticFileInclude
=
397 appEngineWebXml
.includeStaticPattern(path
, expiration
);
399 Map
<String
, String
> httpHeaders
= staticFileInclude
.getHttpHeaders();
400 Iterator
<XmlParser
.Node
> httpHeaderIter
= getNodeIterator(includeNode
, "http-header");
401 while (httpHeaderIter
.hasNext()) {
402 XmlParser
.Node httpHeaderNode
= httpHeaderIter
.next();
403 String name
= httpHeaderNode
.getAttribute("name");
404 String value
= httpHeaderNode
.getAttribute("value");
406 if (httpHeaders
.containsKey(name
)) {
407 throw new AppEngineConfigException("Two http-header elements have the same name.");
410 httpHeaders
.put(name
, value
);
413 appEngineWebXml
.includeResourcePattern(path
);
417 nodeIter
= getNodeIterator(node
, "exclude");
418 while (nodeIter
.hasNext()) {
419 XmlParser
.Node subNode
= nodeIter
.next();
420 String path
= trim(subNode
.getAttribute("path"));
421 if (type
== FileType
.STATIC
) {
422 appEngineWebXml
.excludeStaticPattern(path
);
424 appEngineWebXml
.excludeResourcePattern(path
);
429 private Iterator
<XmlParser
.Node
> getNodeIterator(XmlParser
.Node node
, String filter
) {
430 @SuppressWarnings("unchecked")
431 Iterator
<XmlParser
.Node
> iterator
= node
.iterator(filter
);
435 private void processSystemPropertiesNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
436 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "property");
437 while (nodeIter
.hasNext()) {
438 XmlParser
.Node subNode
= nodeIter
.next();
439 String propertyName
= trim(subNode
.getAttribute("name"));
440 String propertyValue
= trim(subNode
.getAttribute("value"));
441 appEngineWebXml
.addSystemProperty(propertyName
, propertyValue
);
445 private void processBetaSettingsNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
446 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "setting");
447 while (nodeIter
.hasNext()) {
448 XmlParser
.Node subNode
= nodeIter
.next();
449 String name
= trim(subNode
.getAttribute("name"));
450 String value
= trim(subNode
.getAttribute("value"));
451 appEngineWebXml
.addBetaSetting(name
, value
);
455 private void processHealthCheckNode(XmlParser
.Node settingsNode
,
456 AppEngineWebXml appEngineWebXml
) {
457 HealthCheck healthCheck
= appEngineWebXml
.getHealthCheck();
458 String enableHealthCheck
= trim(getChildNodeText(settingsNode
, "enable-health-check"));
459 if (enableHealthCheck
!= null && !enableHealthCheck
.isEmpty()) {
460 healthCheck
.setEnableHealthCheck(toBoolean(enableHealthCheck
));
462 healthCheck
.setCheckIntervalSec(
463 getChildNodePositiveInteger(settingsNode
, "check-interval-sec"));
464 healthCheck
.setTimeoutSec(getChildNodePositiveInteger(settingsNode
, "timeout-sec"));
465 healthCheck
.setUnhealthyThreshold(
466 getChildNodePositiveInteger(settingsNode
, "unhealthy-threshold"));
467 healthCheck
.setHealthyThreshold(
468 getChildNodePositiveInteger(settingsNode
, "healthy-threshold"));
469 healthCheck
.setRestartThreshold(
470 getChildNodePositiveInteger(settingsNode
, "restart-threshold"));
471 healthCheck
.setHost(getChildNodeText(settingsNode
, "host"));
474 private void processResourcesNode(XmlParser
.Node settingsNode
,
475 AppEngineWebXml appEngineWebXml
) {
476 Resources resources
= appEngineWebXml
.getResources();
477 Double cpu
= getChildNodeDouble(settingsNode
, "cpu");
479 resources
.setCpu(cpu
);
481 Double memory_gb
= getChildNodeDouble(settingsNode
, "memory-gb");
482 if (memory_gb
!= null) {
483 resources
.setMemoryGb(memory_gb
);
485 Integer disk_size_gb
= getChildNodePositiveInteger(settingsNode
, "disk-size-gb");
486 if (disk_size_gb
!= null) {
487 resources
.setDiskSizeGb(disk_size_gb
);
491 private void processNetworkNode(XmlParser
.Node settingsNode
,
492 AppEngineWebXml appEngineWebXml
) {
493 Network network
= appEngineWebXml
.getNetwork();
494 String instance_tag
= trim(getChildNodeText(settingsNode
, "instance-tag"));
495 if (instance_tag
!= null && !instance_tag
.isEmpty()) {
496 network
.setInstanceTag(instance_tag
);
498 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(settingsNode
, "forwarded-port");
499 while (nodeIter
.hasNext()) {
500 XmlParser
.Node subNode
= nodeIter
.next();
501 String forwardedPort
= getTextNode(subNode
);
502 network
.addForwardedPort(forwardedPort
);
504 String name
= trim(getChildNodeText(settingsNode
, "name"));
505 if (name
!= null && !name
.isEmpty()) {
506 network
.setName(name
);
510 private void processEnvironmentVariablesNode(
511 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
512 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "env-var");
513 while (nodeIter
.hasNext()) {
514 XmlParser
.Node subNode
= nodeIter
.next();
515 String propertyName
= trim(subNode
.getAttribute("name"));
516 String propertyValue
= trim(subNode
.getAttribute("value"));
517 appEngineWebXml
.addEnvironmentVariable(propertyName
, propertyValue
);
521 private void processPermissionsNode(
522 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
523 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "permission");
524 while (nodeIter
.hasNext()) {
525 XmlParser
.Node subNode
= nodeIter
.next();
526 String className
= trim(subNode
.getAttribute("class"));
527 String name
= trim(subNode
.getAttribute("name"));
528 String actions
= trim(subNode
.getAttribute("actions"));
529 appEngineWebXml
.addUserPermission(className
, name
, actions
);
533 private void processInboundServicesNode(
534 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
535 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "service");
536 while (nodeIter
.hasNext()) {
537 XmlParser
.Node subNode
= nodeIter
.next();
538 String service
= getTextNode(subNode
);
539 appEngineWebXml
.addInboundService(service
);
543 private void processAdminConsoleNode(
544 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
545 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "page");
546 while (nodeIter
.hasNext()) {
547 XmlParser
.Node subNode
= nodeIter
.next();
548 String name
= trim(subNode
.getAttribute("name"));
549 String url
= trim(subNode
.getAttribute("url"));
550 appEngineWebXml
.addAdminConsolePage(new AdminConsolePage(name
, url
));
554 private void processErrorHandlerNode(
555 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
556 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "handler");
557 while (nodeIter
.hasNext()) {
558 XmlParser
.Node subNode
= nodeIter
.next();
559 String file
= trim(subNode
.getAttribute("file"));
560 String errorCode
= trim(subNode
.getAttribute("error-code"));
561 appEngineWebXml
.addErrorHandler(new ErrorHandler(file
, errorCode
));
565 private void processApiConfigNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
566 String servlet
= trim(node
.getAttribute("servlet-class"));
567 String url
= trim(node
.getAttribute("url-pattern"));
568 appEngineWebXml
.setApiConfig(new ApiConfig(servlet
, url
));
571 Iterator
<XmlParser
.Node
> subNodeIter
= getNodeIterator(node
, "endpoint-servlet-mapping-id");
572 while (subNodeIter
.hasNext()) {
573 XmlParser
.Node subNode
= subNodeIter
.next();
574 id
= trim(getTextNode(subNode
));
575 if (id
!= null && id
.length() > 0) {
576 appEngineWebXml
.addApiEndpoint(id
);
581 private void processPagespeedNode(XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
582 Pagespeed pagespeed
= new Pagespeed();
583 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "url-blacklist");
584 while (nodeIter
.hasNext()) {
585 XmlParser
.Node subNode
= nodeIter
.next();
586 String urlMatcher
= getTextNode(subNode
);
587 pagespeed
.addUrlBlacklist(urlMatcher
);
589 nodeIter
= getNodeIterator(node
, "domain-to-rewrite");
590 while (nodeIter
.hasNext()) {
591 XmlParser
.Node subNode
= nodeIter
.next();
592 String domain
= getTextNode(subNode
);
593 pagespeed
.addDomainToRewrite(domain
);
595 nodeIter
= getNodeIterator(node
, "enabled-rewriter");
596 while (nodeIter
.hasNext()) {
597 XmlParser
.Node subNode
= nodeIter
.next();
598 String rewriter
= getTextNode(subNode
);
599 pagespeed
.addEnabledRewriter(rewriter
);
601 nodeIter
= getNodeIterator(node
, "disabled-rewriter");
602 while (nodeIter
.hasNext()) {
603 XmlParser
.Node subNode
= nodeIter
.next();
604 String rewriter
= getTextNode(subNode
);
605 pagespeed
.addDisabledRewriter(rewriter
);
607 appEngineWebXml
.setPagespeed(pagespeed
);
610 private void processClassLoaderConfig(
611 XmlParser
.Node node
, AppEngineWebXml appEngineWebXml
) {
612 ClassLoaderConfig config
= new ClassLoaderConfig();
613 appEngineWebXml
.setClassLoaderConfig(config
);
614 Iterator
<XmlParser
.Node
> nodeIter
= getNodeIterator(node
, "priority-specifier");
615 while (nodeIter
.hasNext()) {
616 processClassPathPrioritySpecifier(nodeIter
.next(), config
);
620 private void processClassPathPrioritySpecifier(Node node
, ClassLoaderConfig config
) {
621 PrioritySpecifierEntry entry
= new PrioritySpecifierEntry();
622 entry
.setFilename(node
.getAttribute("filename"));
623 entry
.setPriority(node
.getAttribute("priority"));
624 entry
.checkClassLoaderConfig();
628 private void processUrlStreamHandler(Node node
, AppEngineWebXml appEngineWebXml
) {
629 appEngineWebXml
.setUrlStreamHandlerType(getTextNode(node
));
632 private boolean getBooleanValue(XmlParser
.Node node
) {
633 return toBoolean(getTextNode(node
));
636 private boolean getBooleanAttributeValue(XmlParser
.Node node
, String attribute
) {
637 return toBoolean(node
.getAttribute(attribute
));
640 private boolean toBoolean(String value
) {
641 value
= value
.trim();
642 return (value
.equalsIgnoreCase("true") || value
.equals("1"));
645 private String
getTextNode(XmlParser
.Node node
) {
646 String value
= (String
) node
.get(0);
653 private String
trim(String attribute
) {
654 return attribute
== null ?
null : attribute
.trim();
657 private void processUseGoogleConnectorJNode(XmlParser
.Node node
,
658 AppEngineWebXml appEngineWebXml
) {
659 appEngineWebXml
.setUseGoogleConnectorJ(getBooleanValue(node
));