1 // Copyright 2008 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
.Stack
;
10 * Creates an {@link CronXml} instance from
11 * <appdir>WEB-INF/cron.xml. If you want to read the configuration
12 * from a different file, subclass and override {@link #getFilename()}. If you
13 * want to read the configuration from something that isn't a file, subclass
14 * and override {@link #getInputStream()}.
17 public class CronXmlReader
extends AbstractConfigXmlReader
<CronXml
> {
19 private static final String FILENAME
= "WEB-INF/cron.xml";
21 private static final String CRONENTRIES_TAG
= "cronentries";
22 private static final String CRON_TAG
= "cron";
23 private static final String DESCRIPTION_TAG
= "description";
24 private static final String SCHEDULE_TAG
= "schedule";
25 private static final String TARGET_TAG
= "target";
26 private static final String TIMEZONE_TAG
= "timezone";
27 private static final String URL_TAG
= "url";
28 private static final String RETRY_PARAMETERS_TAG
= "retry-parameters";
29 private static final String JOB_RETRY_LIMIT_TAG
= "job-retry-limit";
30 private static final String JOB_AGE_LIMIT_TAG
= "job-age-limit";
31 private static final String MIN_BACKOFF_SECONDS_TAG
= "min-backoff-seconds";
32 private static final String MAX_BACKOFF_SECONDS_TAG
= "max-backoff-seconds";
33 private static final String MAX_DOUBLINGS_TAG
= "max-doublings";
36 * Constructs the reader for {@code cron.xml} in a given application directory.
37 * @param appDir the application directory
39 public CronXmlReader(String appDir
) {
44 * Parses the config file.
45 * @return A {@link CronXml} object representing the parsed configuration.
47 public CronXml
readCronXml() {
48 return readConfigXml();
52 protected CronXml
processXml(InputStream is
) {
53 final CronXml cronXml
= new CronXml();
54 parse(new ParserCallback() {
57 boolean insideRetryParametersTag
= false;
60 public void newNode(Node node
, Stack
<Node
> ancestors
) {
61 switch (ancestors
.size()) {
63 if (!CRONENTRIES_TAG
.equalsIgnoreCase(node
.getTag())) {
64 throw new AppEngineConfigException(getFilename() + " does not contain <"
65 + CRONENTRIES_TAG
+ ">");
68 throw new AppEngineConfigException(getFilename() + " contains multiple <"
69 + CRONENTRIES_TAG
+ ">");
75 if (CRON_TAG
.equalsIgnoreCase(node
.getTag())) {
76 entry
= cronXml
.addNewEntry();
78 throw new AppEngineConfigException(getFilename() + " contains <"
79 + node
.getTag() + "> instead of <" + CRON_TAG
+ "/>");
84 insideRetryParametersTag
= false;
85 assert(entry
!= null);
86 if (DESCRIPTION_TAG
.equalsIgnoreCase(node
.getTag())) {
87 if (node
.size() == 1 && node
.get(0) instanceof String
) {
88 entry
.setDescription((String
) node
.get(0));
90 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
91 + DESCRIPTION_TAG
+ ">");
93 } else if (URL_TAG
.equalsIgnoreCase(node
.getTag())) {
94 if (node
.size() == 1 && node
.get(0) instanceof String
) {
95 entry
.setUrl((String
) node
.get(0));
97 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
100 } else if (SCHEDULE_TAG
.equalsIgnoreCase(node
.getTag())) {
101 if (node
.size() == 1 && node
.get(0) instanceof String
) {
102 entry
.setSchedule((String
) node
.get(0));
104 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
105 + SCHEDULE_TAG
+ ">");
107 } else if (TIMEZONE_TAG
.equalsIgnoreCase(node
.getTag())) {
108 if (node
.size() == 1 && node
.get(0) instanceof String
) {
109 entry
.setTimezone((String
) node
.get(0));
111 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
112 + TIMEZONE_TAG
+ ">");
114 } else if (TARGET_TAG
.equalsIgnoreCase(node
.getTag())) {
115 if (node
.size() == 1 && node
.get(0) instanceof String
) {
116 entry
.setTarget((String
) node
.get(0));
118 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
121 } else if (RETRY_PARAMETERS_TAG
.equalsIgnoreCase(node
.getTag())) {
122 entry
.setRetryParameters(new RetryParametersXml());
123 insideRetryParametersTag
= true;
125 throw new AppEngineConfigException(getFilename() + " contains unknown <"
126 + node
.getTag() + "> inside <" + CRON_TAG
+ "/>");
131 assert(insideRetryParametersTag
);
132 assert(entry
!= null);
133 boolean brokenTag
= !(node
.size() == 1 && node
.get(0) instanceof String
);
135 RetryParametersXml retryParameters
= entry
.getRetryParameters();
136 assert(entry
.getRetryParameters() != null);
138 if (JOB_RETRY_LIMIT_TAG
.equalsIgnoreCase(node
.getTag())) {
140 retryParameters
.setRetryLimit(getString(node
));
142 } else if (JOB_AGE_LIMIT_TAG
.equalsIgnoreCase(node
.getTag())) {
144 retryParameters
.setAgeLimitSec(getString(node
));
146 } else if (MIN_BACKOFF_SECONDS_TAG
.equalsIgnoreCase(node
.getTag())) {
148 retryParameters
.setMinBackoffSec(getString(node
));
150 } else if (MAX_BACKOFF_SECONDS_TAG
.equalsIgnoreCase(node
.getTag())) {
152 retryParameters
.setMaxBackoffSec(getString(node
));
154 } else if (MAX_DOUBLINGS_TAG
.equalsIgnoreCase(node
.getTag())) {
156 retryParameters
.setMaxDoublings(getString(node
));
159 throw new AppEngineConfigException(getFilename() + " contains unknown <"
160 + node
.getTag() + "> inside <" + RETRY_PARAMETERS_TAG
+ "/>");
164 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
165 + node
.getTag() + ">");
170 throw new AppEngineConfigException(getFilename()
171 + " has a syntax error; node <"
172 + node
.getTag() + "> is too deeply nested to be valid.");
176 cronXml
.validateLastEntry();
181 protected String
getRelativeFilename() {