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";
30 * Constructs the reader for {@code cron.xml} in a given application directory.
31 * @param appDir the application directory
33 public CronXmlReader(String appDir
) {
38 * Parses the config file.
39 * @return A {@link CronXml} object representing the parsed configuration.
41 public CronXml
readCronXml() {
42 return readConfigXml();
46 protected CronXml
processXml(InputStream is
) {
47 final CronXml cronXml
= new CronXml();
48 parse(new ParserCallback() {
53 public void newNode(Node node
, Stack
<Node
> ancestors
) {
54 switch (ancestors
.size()) {
56 if (!CRONENTRIES_TAG
.equalsIgnoreCase(node
.getTag())) {
57 throw new AppEngineConfigException(getFilename() + " does not contain <"
58 + CRONENTRIES_TAG
+ ">");
61 throw new AppEngineConfigException(getFilename() + " contains multiple <"
62 + CRONENTRIES_TAG
+ ">");
68 if (CRON_TAG
.equalsIgnoreCase(node
.getTag())) {
69 entry
= cronXml
.addNewEntry();
71 throw new AppEngineConfigException(getFilename() + " contains <"
72 + node
.getTag() + "> instead of <" + CRON_TAG
+ "/>");
77 assert(entry
!= null);
78 if (DESCRIPTION_TAG
.equalsIgnoreCase(node
.getTag())) {
79 if (node
.size() == 1 && node
.get(0) instanceof String
) {
80 entry
.setDescription((String
) node
.get(0));
82 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
83 + DESCRIPTION_TAG
+ ">");
85 } else if (URL_TAG
.equalsIgnoreCase(node
.getTag())) {
86 if (node
.size() == 1 && node
.get(0) instanceof String
) {
87 entry
.setUrl((String
) node
.get(0));
89 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
92 } else if (SCHEDULE_TAG
.equalsIgnoreCase(node
.getTag())) {
93 if (node
.size() == 1 && node
.get(0) instanceof String
) {
94 entry
.setSchedule((String
) node
.get(0));
96 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
97 + SCHEDULE_TAG
+ ">");
99 } else if (TIMEZONE_TAG
.equalsIgnoreCase(node
.getTag())) {
100 if (node
.size() == 1 && node
.get(0) instanceof String
) {
101 entry
.setTimezone((String
) node
.get(0));
103 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
104 + TIMEZONE_TAG
+ ">");
106 } else if (TARGET_TAG
.equalsIgnoreCase(node
.getTag())) {
107 if (node
.size() == 1 && node
.get(0) instanceof String
) {
108 entry
.setTarget((String
) node
.get(0));
110 throw new AppEngineConfigException(getFilename() + " has bad contents in <"
114 throw new AppEngineConfigException(getFilename() + " contains unknown <"
115 + node
.getTag() + "> inside <" + CRON_TAG
+ "/>");
120 throw new AppEngineConfigException(getFilename()
121 + " has a syntax error; node <"
122 + node
.getTag() + "> is too deeply nested to be valid.");
126 cronXml
.validateLastEntry();
131 protected String
getRelativeFilename() {