App Engine 1.8.4.
[gae.git] / java / src / main / com / google / apphosting / utils / config / IndexesXmlReader.java
blobf09afae2e7c79ad32d90701c477bce10215153a9
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.File;
7 import java.io.FileNotFoundException;
8 import java.io.FileReader;
9 import java.io.InputStream;
10 import java.io.Reader;
11 import java.util.Stack;
12 import java.util.logging.Level;
14 /**
15 * Creates an {@link IndexesXml} instance from
16 * <appdir>WEB-INF/datastore-indexes.xml. If you want to read the
17 * configuration from a different file, subclass and override
18 * {@link #getFilename()}. If you want to read the configuration from
19 * something that isn't a file, subclass and override
20 * {@link #getInputStream()}.
23 public class IndexesXmlReader extends AbstractConfigXmlReader<IndexesXml> {
25 /**
26 * Relative-to-{@code GenerationDirectory.GENERATED_DIR_PROPERTY} file for
27 * generated index.
29 public static final String GENERATED_INDEX_FILENAME = "datastore-indexes-auto.xml";
30 public static final String INDEX_FILENAME = "datastore-indexes.xml";
31 public static final String INDEX_YAML_FILENAME = "WEB-INF/index.yaml";
33 /** Name of the XML tag in {@code datastore-indexes.xml} for autoindexing */
34 public static final String AUTOINDEX_TAG = "auto-update";
36 private static final String FILENAME = "WEB-INF/datastore-indexes.xml";
38 private static final String INDEXES_TAG = "datastore-indexes";
39 private static final String INDEX_TAG = "datastore-index";
40 private static final String KIND_PROP = "kind";
41 private static final String ANCESTORS_PROP = "ancestor";
42 private static final String ANCESTORS_VALUE_YES = "true";
43 private static final String ANCESTORS_VALUE_NO = "false";
44 private static final String PROPERTY_TAG = "property";
45 private static final String NAME_PROP = "name";
46 private static final String DIRECTION_PROP = "direction";
47 private static final String DIRECTION_VALUE_ASC = "asc";
48 private static final String DIRECTION_VALUE_DESC = "desc";
50 private IndexesXml indexesXml;
52 /**
53 * Constructs a reader for the {@code indexes.xml} configuration of a given app.
54 * @param appDir root directory of the application
56 public IndexesXmlReader(String appDir) {
57 super(appDir, false);
60 /**
61 * Reads the configuration file.
62 * @return an {@link IndexesXml} representing the parsed configuration.
64 public IndexesXml readIndexesXml() {
65 return readConfigXml();
68 /**
69 * Reads the index configuration. If neither the user-generated nor the
70 * auto-generated config file exists, returns a {@code null}. Otherwise,
71 * reads both files (if available) and returns the union of both sets of
72 * indexes.
74 * @throws AppEngineConfigException If the file cannot be parsed properly
76 @Override
77 protected IndexesXml readConfigXml() {
78 InputStream is = null;
79 String filename = null;
81 indexesXml = new IndexesXml();
82 try {
83 if (fileExists()) {
84 filename = getFilename();
85 is = getInputStream();
86 processXml(is);
87 logger.info("Successfully processed " + filename);
89 if (yamlFileExists()) {
90 filename = getYamlFilename();
91 IndexYamlReader.parse(getYamlReader(), indexesXml);
92 logger.info("Successfully processed " + filename);
94 if (generatedFileExists()) {
95 filename = getGeneratedFile().getPath();
96 is = getGeneratedStream();
97 processXml(is);
98 logger.info("Successfully processed " + filename);
100 } catch (Exception e) {
101 String msg = "Received exception processing " + filename;
102 logger.log(Level.SEVERE, msg, e);
103 if (e instanceof AppEngineConfigException) {
104 throw (AppEngineConfigException) e;
106 throw new AppEngineConfigException(msg, e);
107 } finally {
108 close(is);
110 return indexesXml;
113 @Override
114 protected IndexesXml processXml(InputStream is) {
115 parse(new ParserCallback() {
116 boolean first = true;
117 IndexesXml.Index index;
119 @Override
120 public void newNode(Node node, Stack<Node> ancestors) {
121 switch (ancestors.size()) {
122 case 0:
123 if (!INDEXES_TAG.equalsIgnoreCase(node.getTag())) {
124 throw new AppEngineConfigException(getFilename() + " does not contain <"
125 + INDEXES_TAG + ">");
127 if (!first) {
128 throw new AppEngineConfigException(getFilename() + " contains multiple <"
129 + INDEXES_TAG + ">");
131 first = false;
132 break;
134 case 1:
135 if (INDEX_TAG.equalsIgnoreCase(node.getTag())) {
136 String kind = node.getAttribute(KIND_PROP);
137 if (kind == null) {
138 throw new AppEngineConfigException(getFilename() + " has <" + INDEX_TAG +
139 "> missing required attribute \"" + KIND_PROP + "\"");
141 String anc = node.getAttribute(ANCESTORS_PROP).toLowerCase();
142 boolean ancestorProp = false;
143 if (anc != null) {
144 if (anc.equals(ANCESTORS_VALUE_YES)) {
145 ancestorProp = true;
146 } else if (!anc.equals(ANCESTORS_VALUE_NO)) {
147 throw new AppEngineConfigException(getFilename() + " has <" + INDEX_TAG +
148 "> with attribute \"" + ANCESTORS_PROP + "\" not \"" + ANCESTORS_VALUE_YES +
149 "\" or \"" + ANCESTORS_VALUE_NO + "\"");
152 index = indexesXml.addNewIndex(kind, ancestorProp);
154 } else {
155 throw new AppEngineConfigException(getFilename() + " contains <"
156 + node.getTag() + "> instead of <" + INDEX_TAG + "/>");
158 break;
160 case 2:
161 assert(index != null);
162 if (PROPERTY_TAG.equalsIgnoreCase(node.getTag())) {
163 String name = node.getAttribute(NAME_PROP);
164 if (name == null) {
165 throw new AppEngineConfigException(getFilename() + " has <" + PROPERTY_TAG +
166 "> missing required attribute \"" + NAME_PROP + "\"");
168 String direction = node.getAttribute(DIRECTION_PROP).toLowerCase();
169 boolean ascending = true;
170 if (direction != null) {
171 if (direction.equals(DIRECTION_VALUE_DESC)) {
172 ascending = false;
173 } else if (!direction.equals(DIRECTION_VALUE_ASC)) {
174 throw new AppEngineConfigException(getFilename() + " has <" + PROPERTY_TAG +
175 "> with attribute \"" + DIRECTION_PROP + "\" not \"" + DIRECTION_VALUE_ASC +
176 "\" or \"" + DIRECTION_VALUE_DESC + "\"");
179 index.addNewProperty(name, ascending);
180 } else {
181 throw new AppEngineConfigException(getFilename() + " contains <"
182 + node.getTag() + "> instead of <" + PROPERTY_TAG + "/>");
184 break;
186 default:
187 throw new AppEngineConfigException(getFilename()
188 + " has a syntax error; node <"
189 + node.getTag() + "> is too deeply nested to be valid.");
192 }, is);
193 return indexesXml;
196 @Override
197 protected String getRelativeFilename() {
198 return FILENAME;
201 @Override
202 protected File getGeneratedFile() {
203 File genFile = new File(GenerationDirectory.getGenerationDirectory(new File(appDir)),
204 GENERATED_INDEX_FILENAME);
205 return genFile;
208 protected String getYamlFilename() {
209 return appDir + INDEX_YAML_FILENAME;
212 protected boolean yamlFileExists() {
213 return new File(getYamlFilename()).exists();
216 protected Reader getYamlReader() {
217 try {
218 return new FileReader(getYamlFilename());
219 } catch (FileNotFoundException ex) {
220 throw new AppEngineConfigException("Cannot find file" + getYamlFilename());