1 // Copyright 2009 Google Inc. All Rights Reserved.
2 package com
.google
.apphosting
.utils
.config
;
4 import org
.mortbay
.xml
.XmlParser
.Node
;
6 import java
.io
.InputStream
;
7 import java
.util
.ArrayList
;
8 import java
.util
.Stack
;
11 * Creates an {@link QueueXml} instance from
12 * <appdir>WEB-INF/queue.xml. If you want to read the configuration
13 * from a different file, subclass and override {@link #getFilename()}. If you
14 * want to read the configuration from something that isn't a file, subclass
15 * and override {@link #getInputStream()}.
18 public class QueueXmlReader
extends AbstractConfigXmlReader
<QueueXml
> {
20 private static final String FILENAME
= "WEB-INF/queue.xml";
22 private static final String TOTAL_STORAGE_LIMIT_TAG
= "total-storage-limit";
23 private static final String QUEUEENTRIES_TAG
= "queue-entries";
24 private static final String QUEUE_TAG
= "queue";
25 private static final String NAME_TAG
= "name";
26 private static final String RATE_TAG
= "rate";
27 private static final String BUCKET_SIZE
= "bucket-size";
28 private static final String MAX_CONCURRENT_REQUESTS
= "max-concurrent-requests";
29 private static final String MODE_TAG
= "mode";
31 private static final String RETRY_PARAMETERS_TAG
= "retry-parameters";
32 private static final String TASK_RETRY_LIMIT_TAG
= "task-retry-limit";
33 private static final String TASK_AGE_LIMIT_TAG
= "task-age-limit";
34 private static final String MIN_BACKOFF_SECONDS_TAG
= "min-backoff-seconds";
35 private static final String MAX_BACKOFF_SECONDS_TAG
= "max-backoff-seconds";
36 private static final String MAX_DOUBLINGS_TAG
= "max-doublings";
37 private static final String TARGET_TAG
= "target";
39 private static final String ACL_TAG
= "acl";
40 private static final String USER_EMAIL_TAG
= "user-email";
41 private static final String WRITER_EMAIL_TAG
= "writer-email";
44 * Constructs the reader for {@code queue.xml} in a given application directory.
45 * @param appDir the application directory
47 public QueueXmlReader(String appDir
) {
52 * Parses the config file.
53 * @return A {@link QueueXml} object representing the parsed configuration.
55 public QueueXml
readQueueXml() {
56 return readConfigXml();
60 protected QueueXml
processXml(InputStream is
) {
61 final QueueXml queueXml
= new QueueXml();
62 parse(new ParserCallback() {
63 boolean firstQueueEntriesTag
= true;
64 boolean firstTotalStorageLimitTag
= true;
65 boolean insideRetryParametersTag
= false;
66 boolean insideAclTag
= false;
70 public void newNode(Node node
, Stack
<Node
> ancestors
) {
71 switch (ancestors
.size()) {
73 if (QUEUEENTRIES_TAG
.equalsIgnoreCase(node
.getTag())) {
74 if (!firstQueueEntriesTag
) {
75 throw new AppEngineConfigException(getFilename() + " contains multiple <"
76 + QUEUEENTRIES_TAG
+ ">");
78 firstQueueEntriesTag
= false;
83 if (firstQueueEntriesTag
) {
84 throw new AppEngineConfigException(getFilename() + " does not contain <"
85 + QUEUEENTRIES_TAG
+ ">");
87 if (TOTAL_STORAGE_LIMIT_TAG
.equalsIgnoreCase(node
.getTag())) {
88 if (!firstTotalStorageLimitTag
) {
89 throw new AppEngineConfigException(getFilename() + " contains multiple <"
90 + TOTAL_STORAGE_LIMIT_TAG
+ ">");
92 if (node
.size() == 1 && node
.get(0) instanceof String
) {
93 queueXml
.setTotalStorageLimit(getString(node
));
95 throw new AppEngineConfigException(getFilename() + "has invalid <"
96 +TOTAL_STORAGE_LIMIT_TAG
+ ">");
98 firstTotalStorageLimitTag
= false;
99 } else if (QUEUE_TAG
.equalsIgnoreCase(node
.getTag())) {
100 entry
= queueXml
.addNewEntry();
102 throw new AppEngineConfigException(getFilename() + " contains <"
103 + node
.getTag() + "> instead of <" + QUEUE_TAG
+ "/> or <" +
104 TOTAL_STORAGE_LIMIT_TAG
+ ">");
109 assert(entry
!= null);
110 if (NAME_TAG
.equalsIgnoreCase(node
.getTag())) {
111 if (node
.size() == 1 && node
.get(0) instanceof String
) {
112 entry
.setName(getString(node
));
114 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
117 } else if (BUCKET_SIZE
.equalsIgnoreCase(node
.getTag())) {
118 if (node
.size() == 1 && node
.get(0) instanceof String
) {
119 entry
.setBucketSize(getString(node
));
121 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
122 + BUCKET_SIZE
+ ">");
124 } else if (RATE_TAG
.equalsIgnoreCase(node
.getTag())) {
125 if (node
.size() == 1 && node
.get(0) instanceof String
) {
126 entry
.setRate(getString(node
));
128 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
131 } else if (MAX_CONCURRENT_REQUESTS
.equalsIgnoreCase(node
.getTag())) {
132 if (node
.size() == 1 && node
.get(0) instanceof String
) {
133 entry
.setMaxConcurrentRequests(getString(node
));
135 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
136 + MAX_CONCURRENT_REQUESTS
+ ">");
138 } else if (MODE_TAG
.equalsIgnoreCase(node
.getTag())) {
139 if (node
.size() == 1 && node
.get(0) instanceof String
) {
140 entry
.setMode(getString(node
));
142 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
145 } else if (TARGET_TAG
.equalsIgnoreCase(node
.getTag())) {
146 if (node
.size() == 1 && node
.get(0) instanceof String
) {
147 entry
.setTarget(getString(node
));
149 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
152 } else if (RETRY_PARAMETERS_TAG
.equalsIgnoreCase(node
.getTag())) {
153 entry
.setRetryParameters(new QueueXml
.RetryParameters());
154 insideRetryParametersTag
= true;
155 insideAclTag
= false;
156 } else if (ACL_TAG
.equalsIgnoreCase(node
.getTag())) {
157 entry
.setAcl(new ArrayList
<QueueXml
.AclEntry
>());
159 insideRetryParametersTag
= false;
161 throw new AppEngineConfigException(getFilename() + " contains unknown <"
162 + node
.getTag() + "> inside <" + QUEUE_TAG
+ "/>");
167 assert(insideRetryParametersTag ^ insideAclTag
);
168 assert(entry
!= null);
169 boolean brokenTag
= !(node
.size() == 1 && node
.get(0) instanceof String
);
171 if (insideRetryParametersTag
) {
172 assert(entry
.getRetryParameters() != null);
173 QueueXml
.RetryParameters retryParameters
= entry
.getRetryParameters();
175 if (TASK_RETRY_LIMIT_TAG
.equalsIgnoreCase(node
.getTag())) {
177 retryParameters
.setRetryLimit(getString(node
));
179 } else if (TASK_AGE_LIMIT_TAG
.equalsIgnoreCase(node
.getTag())) {
181 retryParameters
.setAgeLimitSec(getString(node
));
183 } else if (MIN_BACKOFF_SECONDS_TAG
.equalsIgnoreCase(node
.getTag())) {
185 retryParameters
.setMinBackoffSec(getString(node
));
187 } else if (MAX_BACKOFF_SECONDS_TAG
.equalsIgnoreCase(node
.getTag())) {
189 retryParameters
.setMaxBackoffSec(getString(node
));
191 } else if (MAX_DOUBLINGS_TAG
.equalsIgnoreCase(node
.getTag())) {
193 retryParameters
.setMaxDoublings(getString(node
));
196 throw new AppEngineConfigException(getFilename() + " contains unknown <"
197 + node
.getTag() + "> inside <" + RETRY_PARAMETERS_TAG
+ "/>");
202 assert(entry
.getAcl() != null);
203 if (USER_EMAIL_TAG
.equalsIgnoreCase(node
.getTag())) {
205 QueueXml
.AclEntry acl
= new QueueXml
.AclEntry();
206 acl
.setUserEmail(getString(node
));
207 entry
.getAcl().add(acl
);
209 } else if (WRITER_EMAIL_TAG
.equalsIgnoreCase(node
.getTag())) {
211 QueueXml
.AclEntry acl
= new QueueXml
.AclEntry();
212 acl
.setWriterEmail(getString(node
));
213 entry
.getAcl().add(acl
);
216 throw new AppEngineConfigException(getFilename() + " contains unknown <"
217 + node
.getTag() + "> inside <" + ACL_TAG
+ "/>");
222 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
223 + node
.getTag() + ">");
228 throw new AppEngineConfigException(getFilename()
229 + " has a syntax error; node <"
230 + node
.getTag() + "> is too deeply nested to be valid.");
234 queueXml
.validateLastEntry();
239 protected String
getRelativeFilename() {