App Engine Java SDK version 1.9.25
[gae.git] / java / src / main / com / google / apphosting / utils / config / IndexesXmlReader.java
blobb2a38af4d3270119b24ac2b7c8d0e5f98f751cb9
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 INDEX_TAG = "datastore-index";
46 public static final String KIND_PROP = "kind";
47 public static final String ANCESTOR_PROP = "ancestor";
48 public static final String PROPERTY_TAG = "property";
49 public static final String NAME_PROP = "name";
50 public static final String DIRECTION_PROP = "direction";
51 public static final String MODE_PROP = "mode";
53 private IndexesXml indexesXml;
55 /**
56 * Constructs a reader for the {@code indexes.xml} configuration of a given app.
57 * @param appDir root directory of the application
59 public IndexesXmlReader(String appDir) {
60 super(appDir, false);
63 /**
64 * Reads the configuration file.
65 * @return an {@link IndexesXml} representing the parsed configuration.
67 public IndexesXml readIndexesXml() {
68 return readConfigXml();
71 /**
72 * Reads the index configuration. If neither the user-written nor the
73 * auto-generated config file exists, returns a {@code null}. Otherwise,
74 * reads both files (if available) and returns the union of both sets of
75 * indexes.
77 * @throws AppEngineConfigException If the file cannot be parsed properly
79 @Override
80 protected IndexesXml readConfigXml() {
81 InputStream is = null;
82 String filename = null;
84 indexesXml = new IndexesXml();
85 try {
86 if (fileExists()) {
87 filename = getFilename();
88 is = getInputStream();
89 processXml(is);
90 logger.info("Successfully processed " + filename);
92 if (yamlFileExists()) {
93 filename = getYamlFilename();
94 IndexYamlReader.parse(getYamlReader(), indexesXml);
95 logger.info("Successfully processed " + filename);
97 if (generatedFileExists()) {
98 filename = getAutoFilename();
99 is = getGeneratedStream();
100 processXml(is);
101 logger.info("Successfully processed " + filename);
103 } catch (Exception e) {
104 String msg = "Received exception processing " + filename;
105 logger.log(Level.SEVERE, msg, e);
106 if (e instanceof AppEngineConfigException) {
107 throw (AppEngineConfigException) e;
109 throw new AppEngineConfigException(msg, e);
110 } finally {
111 close(is);
113 return indexesXml;
116 @Override
117 protected IndexesXml processXml(InputStream is) {
118 parse(new ParserCallback() {
119 IndexesXml.Index index;
120 IndexesXml.Type indexType = null;
123 * Assembles index definitions during XML parsing, and
124 * performs additional validation that was not already done by
125 * the XML Schema.
126 * <p>
127 * There are two types of index definition: "ordered" and
128 * "geo-spatial". For an ordered index, an "ancestor"
129 * specification is optional, and all "property" elements may
130 * optionally specify a "direction", but may not specify a
131 * "mode". In a geo-spatial index, "ancestor" is irrelevant
132 * (and therefore disallowed), and property elements may
133 * specify a mode, but may not specify a direction.
135 @Override
136 public void newNode(Node node, Stack<Node> unusedContainingElements) {
137 if (INDEX_TAG.equalsIgnoreCase(node.getTag())) {
138 String kind = node.getAttribute(KIND_PROP);
139 Boolean ancestorProp = null;
140 String anc = node.getAttribute(ANCESTOR_PROP);
141 if (anc == null) {
142 indexType = null;
143 } else {
144 indexType = IndexesXml.Type.ORDERED;
145 ancestorProp = anc.equals("true") || anc.equals("1");
147 index = indexesXml.addNewIndex(kind, ancestorProp);
148 } else if (PROPERTY_TAG.equalsIgnoreCase(node.getTag())) {
149 assert(index != null);
150 String name = node.getAttribute(NAME_PROP);
151 String direction = node.getAttribute(DIRECTION_PROP);
152 String mode = node.getAttribute(MODE_PROP);
154 if (direction != null) {
155 if (mode != null || indexType == IndexesXml.Type.GEO_SPATIAL) {
156 throw new AppEngineConfigException(
157 "The 'direction' attribute may not be specified in a 'geospatial' index.");
159 indexType = IndexesXml.Type.ORDERED;
160 } else if (mode != null) {
161 if (indexType == IndexesXml.Type.ORDERED) {
162 throw new AppEngineConfigException(
163 "The 'mode' attribute may not be specified with 'direction' or 'ancestor'.");
165 indexType = IndexesXml.Type.GEO_SPATIAL;
168 index.addNewProperty(name, direction, mode);
171 }, is);
172 return indexesXml;
175 @Override
176 protected String getRelativeFilename() {
177 return FILENAME;
180 protected File getGeneratedFile() {
181 File genFile = new File(GenerationDirectory.getGenerationDirectory(new File(appDir)),
182 GENERATED_INDEX_FILENAME);
183 return genFile;
186 public String getAutoFilename() {
187 return getGeneratedFile().getPath();
190 protected boolean generatedFileExists() {
191 return getGeneratedFile().exists();
194 protected InputStream getGeneratedStream() throws Exception {
195 return new FileInputStream(getGeneratedFile());
198 protected String getYamlFilename() {
199 return appDir + INDEX_YAML_FILENAME;
202 protected boolean yamlFileExists() {
203 return new File(getYamlFilename()).exists();
206 protected Reader getYamlReader() {
207 try {
208 return new FileReader(getYamlFilename());
209 } catch (FileNotFoundException ex) {
210 throw new AppEngineConfigException("Cannot find file" + getYamlFilename());