Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / apphosting / utils / config / IndexesXmlReader.java
blobb25d7d32de714195f02547eb148d182952efdaf1
1 package com.google.apphosting.utils.config;
3 import org.mortbay.xml.XmlParser.Node;
5 import java.io.File;
6 import java.io.FileInputStream;
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 * 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()}.
22 * This class performs some validation on the XML, but it does rely on
23 * the fact that (by the time our readIndexesXml() method is called)
24 * the XML has already been partially validated by the XML Schema:
25 * <pre>
26 * java/com/google/appengine/tools/development/datastore-indexes.xsd
27 * </pre>
29 public class IndexesXmlReader extends AbstractConfigXmlReader<IndexesXml> {
31 /**
32 * Relative-to-{@code GenerationDirectory.GENERATED_DIR_PROPERTY} file for
33 * generated index.
35 public static final String GENERATED_INDEX_FILENAME = "datastore-indexes-auto.xml";
36 public static final String INDEX_FILENAME = "datastore-indexes.xml";
37 public static final String INDEX_YAML_FILENAME = "WEB-INF/index.yaml";
39 /** Name of the XML tag in {@code datastore-indexes.xml} for autoindexing */
40 public static final String AUTOINDEX_TAG = "autoGenerate";
42 private static final String FILENAME = "WEB-INF/datastore-indexes.xml";
44 private static final String INDEXES_TAG = "datastore-indexes";
45 private static final String INDEX_TAG = "datastore-index";
46 private static final String KIND_PROP = "kind";
47 private static final String ANCESTORS_PROP = "ancestor";
48 private static final String PROPERTY_TAG = "property";
49 private static final String NAME_PROP = "name";
50 private static final String DIRECTION_PROP = "direction";
51 private static final String DIRECTION_VALUE_ASC = "asc";
52 private static final String DIRECTION_VALUE_DESC = "desc";
53 private static final String MODE_PROP = "mode";
55 private IndexesXml indexesXml;
57 /**
58 * Constructs a reader for the {@code indexes.xml} configuration of a given app.
59 * @param appDir root directory of the application
61 public IndexesXmlReader(String appDir) {
62 super(appDir, false);
65 /**
66 * Reads the configuration file.
67 * @return an {@link IndexesXml} representing the parsed configuration.
69 public IndexesXml readIndexesXml() {
70 return readConfigXml();
73 /**
74 * Reads the index configuration. If neither the user-written nor the
75 * auto-generated config file exists, returns a {@code null}. Otherwise,
76 * reads both files (if available) and returns the union of both sets of
77 * indexes.
79 * @throws AppEngineConfigException If the file cannot be parsed properly
81 @Override
82 protected IndexesXml readConfigXml() {
83 InputStream is = null;
84 String filename = null;
86 indexesXml = new IndexesXml();
87 try {
88 if (fileExists()) {
89 filename = getFilename();
90 is = getInputStream();
91 processXml(is);
92 logger.info("Successfully processed " + filename);
94 if (yamlFileExists()) {
95 filename = getYamlFilename();
96 IndexYamlReader.parse(getYamlReader(), indexesXml);
97 logger.info("Successfully processed " + filename);
99 if (generatedFileExists()) {
100 filename = getAutoFilename();
101 is = getGeneratedStream();
102 processXml(is);
103 logger.info("Successfully processed " + filename);
105 } catch (Exception e) {
106 String msg = "Received exception processing " + filename;
107 logger.log(Level.SEVERE, msg, e);
108 if (e instanceof AppEngineConfigException) {
109 throw (AppEngineConfigException) e;
111 throw new AppEngineConfigException(msg, e);
112 } finally {
113 close(is);
115 return indexesXml;
118 @Override
119 protected IndexesXml processXml(InputStream is) {
120 parse(new ParserCallback() {
121 IndexesXml.Index index;
122 IndexesXml.Type indexType = null;
125 * Assembles index definitions during XML parsing, and
126 * performs additional validation that was not already done by
127 * the XML Schema.
128 * <p>
129 * There are two types of index definition: "ordered" and
130 * "geo-spatial". For an ordered index, an "ancestor"
131 * specification is optional, and all "property" elements may
132 * optionally specify a "direction", but may not specify a
133 * "mode". In a geo-spatial index, "ancestor" is irrelevant
134 * (and therefore disallowed), and property elements may
135 * specify a mode, but may not specify a direction.
137 @Override
138 public void newNode(Node node, Stack<Node> unusedContainingElements) {
139 if (INDEX_TAG.equalsIgnoreCase(node.getTag())) {
140 String kind = node.getAttribute(KIND_PROP);
141 Boolean ancestorProp = null;
142 String anc = node.getAttribute(ANCESTORS_PROP);
143 if (anc == null) {
144 indexType = null;
145 } else {
146 indexType = IndexesXml.Type.ORDERED;
147 ancestorProp = anc.equals("true") || anc.equals("1");
149 index = indexesXml.addNewIndex(kind, ancestorProp);
150 } else if (PROPERTY_TAG.equalsIgnoreCase(node.getTag())) {
151 assert(index != null);
152 String name = node.getAttribute(NAME_PROP);
153 String direction = node.getAttribute(DIRECTION_PROP);
154 String mode = node.getAttribute(MODE_PROP);
156 if (direction != null) {
157 if (mode != null || indexType == IndexesXml.Type.GEO_SPATIAL) {
158 throw new AppEngineConfigException(
159 "The 'direction' attribute may not be specified in a 'geospatial' index.");
161 indexType = IndexesXml.Type.ORDERED;
162 } else if (mode != null) {
163 if (indexType == IndexesXml.Type.ORDERED) {
164 throw new AppEngineConfigException(
165 "The 'mode' attribute may not be specified with 'direction' or 'ancestor'.");
167 indexType = IndexesXml.Type.GEO_SPATIAL;
170 index.addNewProperty(name, direction, mode);
173 }, is);
174 return indexesXml;
177 @Override
178 protected String getRelativeFilename() {
179 return FILENAME;
182 protected File getGeneratedFile() {
183 File genFile = new File(GenerationDirectory.getGenerationDirectory(new File(appDir)),
184 GENERATED_INDEX_FILENAME);
185 return genFile;
188 public String getAutoFilename() {
189 return getGeneratedFile().getPath();
192 protected boolean generatedFileExists() {
193 return getGeneratedFile().exists();
196 protected InputStream getGeneratedStream() throws Exception {
197 return new FileInputStream(getGeneratedFile());
200 protected String getYamlFilename() {
201 return appDir + INDEX_YAML_FILENAME;
204 protected boolean yamlFileExists() {
205 return new File(getYamlFilename()).exists();
208 protected Reader getYamlReader() {
209 try {
210 return new FileReader(getYamlFilename());
211 } catch (FileNotFoundException ex) {
212 throw new AppEngineConfigException("Cannot find file" + getYamlFilename());