1 package com
.google
.apphosting
.utils
.config
;
3 import org
.mortbay
.xml
.XmlParser
.Node
;
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
;
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:
26 * java/com/google/appengine/tools/development/datastore-indexes.xsd
29 public class IndexesXmlReader
extends AbstractConfigXmlReader
<IndexesXml
> {
32 * Relative-to-{@code GenerationDirectory.GENERATED_DIR_PROPERTY} file for
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
;
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
) {
64 * Reads the configuration file.
65 * @return an {@link IndexesXml} representing the parsed configuration.
67 public IndexesXml
readIndexesXml() {
68 return readConfigXml();
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
77 * @throws AppEngineConfigException If the file cannot be parsed properly
80 protected IndexesXml
readConfigXml() {
81 InputStream is
= null;
82 String filename
= null;
84 indexesXml
= new IndexesXml();
87 filename
= getFilename();
88 is
= getInputStream();
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();
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
);
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
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.
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
);
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
);
176 protected String
getRelativeFilename() {
180 protected File
getGeneratedFile() {
181 File genFile
= new File(GenerationDirectory
.getGenerationDirectory(new File(appDir
)),
182 GENERATED_INDEX_FILENAME
);
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() {
208 return new FileReader(getYamlFilename());
209 } catch (FileNotFoundException ex
) {
210 throw new AppEngineConfigException("Cannot find file" + getYamlFilename());