001: /*
002: * Created on 31-Mar-2004
003: *
004: */
005: package com.jofti.config;
006:
007: import java.io.IOException;
008: import java.io.InputStream;
009: import java.util.ArrayList;
010: import java.util.HashMap;
011: import java.util.List;
012: import java.util.Map;
013: import java.util.Properties;
014:
015: import org.xmlpull.v1.XmlPullParser;
016: import org.xmlpull.v1.XmlPullParserException;
017: import org.xmlpull.v1.XmlPullParserFactory;
018:
019: import com.jofti.exception.JoftiException;
020:
021: /**
022:
023: *
024: * The parser is responsible for parsing the XMl configuration file and returns a Map of IndexConfig objects (one for each index in the file).<p>
025: * The current implementation uses KXML as its parser (as it is small and we probably will not get any version interference with
026: * other XML parsers).
027: *
028: * @author Steve Woodcock (steve@jofti.com)<p>
029: * @version 1.0
030: */
031: public class ConfigFileParser {
032:
033: /**
034: * Parses the config file from the file name. The method uses getResourceAsStream() so the filename must be
035: * on the classpath.<p>
036: *
037: * @param configFile
038: * @return Map of IndexConfig objects.
039: * @throws JoftiException
040: */
041: public Map parseIndexConfig(String configFile)
042: throws JoftiException {
043:
044: Map temp = null;
045:
046: InputStream stream = null;
047:
048: try {
049: stream = Thread.currentThread().getContextClassLoader()
050: .getResourceAsStream(configFile);
051:
052: if (stream == null) {
053: throw new JoftiException(
054: "Unable to find config file '"
055: + configFile
056: + "' on classpath - ensure the file name is correctly configured");
057: }
058: temp = parseConfig(stream);
059:
060: } catch (JoftiException e) {
061: e.printStackTrace();
062: } catch (Throwable t) {
063: t.printStackTrace();
064: } finally {
065:
066: if (stream != null) {
067: try {
068: stream.close();
069: } catch (Exception e) {
070: // we do not care about this
071: }
072: }
073: }
074: return temp;
075: }
076:
077: /**
078: * Parses the config file from an inputstream. The method allows calling code to load the configfile however it wants and
079: * pass the location independent stream containing the file.
080: * on the classpath.<p>
081: *
082: * @param stream
083: * @return Map of IndexConfig objects.
084: * @throws JoftiException
085: */
086: public Map parseIndexConfig(InputStream stream)
087: throws JoftiException {
088:
089: Map temp = null;
090:
091: try {
092:
093: if (stream == null) {
094: throw new JoftiException(
095: "InputStream is null ensure the file name is correctly configured");
096: }
097: temp = parseConfig(stream);
098:
099: } catch (JoftiException e) {
100: e.printStackTrace();
101: } catch (Throwable t) {
102: t.printStackTrace();
103: } finally {
104:
105: if (stream != null) {
106: try {
107: stream.close();
108: } catch (Exception e) {
109: // we do not care about this
110: }
111: }
112: }
113: return temp;
114: }
115:
116: private XmlPullParser getParser() throws Exception {
117: return XmlPullParserFactory.newInstance(
118: "org.kxml2.io.KXmlParser",
119: Thread.currentThread().getContextClassLoader()
120: .getClass()).newPullParser();
121:
122: }
123:
124: private Map parseConfig(InputStream stream) throws JoftiException {
125: Map indexMap = new HashMap();
126:
127: XmlPullParser parser = null;
128:
129: try {
130:
131: if (stream == null) {
132: throw new JoftiException(
133: "InputStrean is null - ensure the file name is correctly configured");
134: }
135:
136: parser = getParser();
137:
138: parser.setInput(stream, null);
139:
140: int eventType = parser.getEventType();
141: do {
142: if (eventType == XmlPullParser.START_DOCUMENT) {
143:
144: indexMap = processIndexes(parser);
145:
146: }
147:
148: if (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
149: eventType = parser.next();
150: }
151: } while (eventType != XmlPullParser.END_DOCUMENT);
152: parser.setInput(null);
153: } catch (JoftiException e) {
154:
155: e.printStackTrace();
156: } catch (XmlPullParserException e) {
157:
158: e.printStackTrace();
159: } catch (IOException e) {
160:
161: e.printStackTrace();
162: } catch (Throwable t) {
163: t.printStackTrace();
164: } finally {
165:
166: if (stream != null) {
167: try {
168: stream.close();
169: } catch (Exception e) {
170: // we do not care about this
171: }
172: }
173: }
174:
175: return indexMap;
176: }
177:
178: private Map processIndexes(XmlPullParser parser)
179: throws XmlPullParserException, IOException, JoftiException {
180:
181: Map temp = new HashMap();
182: //get the next tag - this should be indexes
183: while (true) {
184: int tagType = parser.nextTag();
185:
186: if (tagType == XmlPullParser.START_TAG) {
187: String name = parser.getName();
188:
189: if ("indexes".equalsIgnoreCase(name)) {
190: if (!parser.isEmptyElementTag()) {
191: // the next one should be an index tag
192: tagType = parser.nextTag();
193: // should be index
194: while (tagType == XmlPullParser.START_TAG
195: && "index".equalsIgnoreCase(parser
196: .getName())) {
197: IndexConfig config = processIndex(parser);
198: temp.put(config.getName(), config);
199: parser.nextTag();
200: }
201:
202: } else {
203: throw new JoftiException(
204: "Indexes definition in config file is empty");
205: }
206: } else {
207: throw new JoftiException("Unrecognised tag " + name
208: + " at root of document");
209: }
210: } else if (tagType == XmlPullParser.END_TAG) {
211: break;
212: }
213:
214: //parser.nextTag();
215: parser.require(XmlPullParser.END_TAG, null, "indexes");
216: break;
217: }
218: return temp;
219: }
220:
221: IndexConfig processIndex(XmlPullParser parser) throws IOException,
222: XmlPullParserException, JoftiException {
223:
224: DefaultIndexConfig config = new DefaultIndexConfig();
225:
226: String name = parser.getName();
227:
228: if ("index".equalsIgnoreCase(name)) {
229:
230: //process the attributes for the index tag
231: int numAttributes = parser.getAttributeCount();
232: if (numAttributes >= 0) {
233: for (int i = 0; i < numAttributes; i++) {
234: String attributeName = parser.getAttributeName(i);
235: if (attributeName.equalsIgnoreCase("name")) {
236: config.setName(parser.getAttributeValue(i));
237: } else if (attributeName
238: .equalsIgnoreCase("init-method")) {
239: //config.setName(parser.getAttributeValue(i));
240: } else if (attributeName
241: .equalsIgnoreCase("destroy-method")) {
242: //config.setName(parser.getAttributeValue(i));
243: } else {
244: //log warning here
245: }
246: }
247: }
248:
249: // now we have either cache/classes/type
250: int event = parser.nextTag();
251:
252: name = parser.getName();
253: while ((event == XmlPullParser.START_TAG)
254: && ("cache".equalsIgnoreCase(name)
255: || "classes".equalsIgnoreCase(name)
256: || "parser".equalsIgnoreCase(name)
257: || "type".equalsIgnoreCase(name)
258: || "disk-overflow".equalsIgnoreCase(name) || "queries"
259: .equalsIgnoreCase(name))) {
260:
261: if (name.equalsIgnoreCase("cache")) {
262: //see if empty
263: config = processCache(parser, config);
264: parser.require(XmlPullParser.END_TAG, "", "cache");
265:
266: } else if (name.equalsIgnoreCase("classes")) {
267: config = (DefaultIndexConfig) processClasses(
268: parser, config);
269: parser
270: .require(XmlPullParser.END_TAG, "",
271: "classes");
272:
273: } else if (name.equalsIgnoreCase("queries")) {
274: config = (DefaultIndexConfig) processQueries(
275: parser, config);
276: parser
277: .require(XmlPullParser.END_TAG, "",
278: "queries");
279:
280: } else if (name.equalsIgnoreCase("parser")) {
281: config.setParserType(parser.nextText().trim());
282: parser.require(XmlPullParser.END_TAG, "", "parser");
283:
284: } else if (name.equalsIgnoreCase("type")) {
285: config.setIndexType(parser.nextText().trim());
286: parser.require(XmlPullParser.END_TAG, "", "type");
287: } else if (name.equalsIgnoreCase("disk-overflow")) {
288: config = (DefaultIndexConfig) processOverflow(
289: parser, config);
290: parser.require(XmlPullParser.END_TAG, "",
291: "disk-overflow");
292: }
293:
294: event = parser.nextTag();
295: name = parser.getName();
296:
297: }
298:
299: parser.require(XmlPullParser.END_TAG, "", "index");
300:
301: }
302: return config;
303: }
304:
305: private DefaultIndexConfig processCache(XmlPullParser parser,
306: DefaultIndexConfig config) throws IOException,
307: XmlPullParserException, JoftiException {
308:
309: // we may have an adapter here
310: int event = parser.nextTag();
311: String name = parser.getName();
312: if (name.equalsIgnoreCase("adapter")) {
313: int adapterAttribs = parser.getAttributeCount();
314: for (int i = 0; i < adapterAttribs; i++) {
315: String temp = parser.getAttributeName(i);
316: if (temp.equalsIgnoreCase("init-method")) {
317: //config.setCacheInitMethod(parser.getAttributeValue(i));
318: } else if (temp.equalsIgnoreCase("destroy-method")) {
319: //config.setCacheDestroyMethod(parser.getAttributeValue(i));
320: } else {
321: //log warning here
322: }
323: }
324: //now see if we have a type or an end tag
325: event = parser.nextTag();
326: while (event == XmlPullParser.START_TAG) {
327: //must be the type tag
328: name = parser.getName();
329: if (name.equalsIgnoreCase("type")) {
330: String content = parser.nextText();
331: config.setCacheAdapter(content.trim());
332: parser.require(XmlPullParser.END_TAG, "", "type");
333: event = parser.nextTag();
334: name = parser.getName();
335: }
336: if (name.equalsIgnoreCase("init-properties")) {
337: Properties props = new Properties();
338: if (!parser.isEmptyElementTag()) {
339: //loop through attributes
340: while (parser.nextTag() == XmlPullParser.START_TAG) {
341: name = parser.getName();
342: if (name.equalsIgnoreCase("property")) {
343: String temp = parser
344: .getAttributeValue(0);
345: if (temp != null) {
346: props.put(temp, parser.nextText()
347: .trim());
348: }
349: }
350: parser.require(XmlPullParser.END_TAG, "",
351: name);
352: }
353: config.setAdapterProperties(props);
354: //assert the
355: parser.require(XmlPullParser.END_TAG, "",
356: "init-properties");
357: event = parser.nextTag();
358:
359: }
360:
361: }
362: }
363:
364: parser.require(XmlPullParser.END_TAG, "", "adapter");
365: parser.nextTag();
366: }
367:
368: return config;
369: }
370:
371: private IndexConfig processOverflow(XmlPullParser parser,
372: IndexConfig config) throws IOException,
373: XmlPullParserException, JoftiException {
374:
375: Properties props = config.getIndexProperties();
376: //now see if we have a type or an end tag
377: int event = parser.nextTag();
378: while (event == XmlPullParser.START_TAG) {
379: //must be the type tag
380: String name = parser.getName();
381: if (name.equalsIgnoreCase("provider")) {
382: String content = parser.nextText();
383: if (content != null) {
384: content = content.trim();
385: }
386: props.put("provider", content);
387: parser.require(XmlPullParser.END_TAG, "", "provider");
388: event = parser.nextTag();
389: name = parser.getName();
390: }
391:
392: if (name.equalsIgnoreCase("init-properties")) {
393:
394: if (!parser.isEmptyElementTag()) {
395: //loop through attributes
396: while (parser.nextTag() == XmlPullParser.START_TAG) {
397: name = parser.getName();
398: if (name.equalsIgnoreCase("property")) {
399: String temp = parser.getAttributeValue(0);
400: if (temp != null) {
401: props.put(temp, parser.nextText()
402: .trim());
403: }
404: }
405: parser.require(XmlPullParser.END_TAG, "", name);
406: }
407:
408: //assert the
409: parser.require(XmlPullParser.END_TAG, "",
410: "init-properties");
411: event = parser.nextTag();
412:
413: }
414:
415: }
416: }
417:
418: return config;
419: }
420:
421: private IndexConfig processClasses(XmlPullParser parser,
422: IndexConfig config) throws IOException,
423: XmlPullParserException, JoftiException {
424:
425: Map temp = new HashMap();
426: //loop through classes
427: while (parser.nextTag() == XmlPullParser.START_TAG) {
428: String name = parser.getName();
429: if (name.equalsIgnoreCase("class")) {
430: List list = new ArrayList();
431: String className = parser.getAttributeValue(0);
432: if (!parser.isEmptyElementTag()) {
433: ;
434: //loop through attributes
435: while (parser.nextTag() == XmlPullParser.START_TAG) {
436: name = parser.getName();
437: if (name.equalsIgnoreCase("property")) {
438: list.add(parser.nextText());
439: }
440: parser.require(XmlPullParser.END_TAG, "", name);
441: }
442: //assert the
443: parser.require(XmlPullParser.END_TAG, "", "class");
444: temp.put(className, list);
445: }
446: config.addMapping(className, list);
447: }
448: }
449:
450: return config;
451: }
452:
453: private IndexConfig processQueries(XmlPullParser parser,
454: IndexConfig config) throws IOException,
455: XmlPullParserException, JoftiException {
456:
457: //loop through queries
458: while (parser.nextTag() == XmlPullParser.START_TAG) {
459: String name = parser.getName();
460: if (name.equalsIgnoreCase("query")) {
461: String queryText = null;
462: String queryName = parser.getAttributeValue(0);
463:
464: queryText = parser.nextText().trim();
465:
466: config.addQuery(queryName, queryText);
467: parser.require(XmlPullParser.END_TAG, "", "query");
468: }
469: }
470:
471: return config;
472: }
473:
474: /**
475: * Used to parse only the classmappings fragment of an xml file. Mot common usage is when manual configuration
476: * is being used with no config file and a file containing only the classes is passed in.<p>
477: * @param config
478: * @param stream
479: * @return IndexConfig containing the parsed classes.
480: * @throws JoftiException
481: */
482: public IndexConfig parseClassMapping(IndexConfig config,
483: InputStream stream) throws JoftiException {
484: return parseClassFromStream(config, stream);
485: }
486:
487: /**
488: * Used to parse only the classmappings fragment of an xml file. Mot common usage is when manual configuration
489: * is being used with no config file and a file containing only the classes is passed in. Note: uses getResourceAsStream and so
490: * file name must be on classpath.<p>
491: * @param config
492: * @param classFileName
493: * @return IndexConfig containing the parsed classes.
494: * @throws JoftiException
495: */
496: public IndexConfig parseClassMapping(IndexConfig config,
497: String classFileName) throws JoftiException {
498: InputStream stream = null;
499: try {
500: stream = Thread.currentThread().getContextClassLoader()
501: .getResourceAsStream(classFileName);
502:
503: if (stream == null) {
504: throw new JoftiException(
505: "Unable to find config file '"
506: + classFileName
507: + "' on classpath - ensure the file name is correctly configured");
508: }
509: config = parseClassFromStream(config, stream);
510: } catch (Throwable t) {
511: if (t instanceof JoftiException) {
512: throw (JoftiException) t;
513: } else {
514: throw new JoftiException(t);
515: }
516: }
517: return config;
518:
519: }
520:
521: private IndexConfig parseClassFromStream(IndexConfig config,
522: InputStream stream) throws JoftiException {
523:
524: try {
525:
526: if (stream == null) {
527: throw new JoftiException(
528: "Input Stream for classes is null");
529: }
530:
531: XmlPullParser parser = getParser();
532: parser.setInput(stream, null);
533:
534: int eventType = parser.getEventType();
535:
536: do {
537: if (eventType == XmlPullParser.START_DOCUMENT) {
538:
539: eventType = parser.nextTag();
540: String name = parser.getName();
541: while (eventType == XmlPullParser.START_TAG) {
542:
543: if (eventType == XmlPullParser.START_TAG
544: && ("classes".equalsIgnoreCase(name))) {
545:
546: config = processClasses(parser, config);
547:
548: parser.require(XmlPullParser.END_TAG, "",
549: "classes");
550: eventType = parser.next();
551: name = parser.getName();
552:
553: } else if (eventType == XmlPullParser.START_TAG
554: && ("queries".equalsIgnoreCase(name))) {
555: config = processQueries(parser, config);
556:
557: parser.require(XmlPullParser.END_TAG, "",
558: "queries");
559: eventType = parser.next();
560: name = parser.getName();
561: } else if (eventType == XmlPullParser.START_TAG
562: && ("disk-overflow"
563: .equalsIgnoreCase(name))) {
564: config = processOverflow(parser, config);
565: parser.require(XmlPullParser.END_TAG, "",
566: "disk-overflow");
567: eventType = parser.next();
568: name = parser.getName();
569: } else {
570: throw new JoftiException(
571: "Classes definitions must contain only '<classes>' or '<queries>' or '<disk-overflow>' tag");
572: }
573: }
574:
575: }
576:
577: //eventType = parser.getEventType();
578: if (eventType != XmlPullParser.END_DOCUMENT) {
579: throw new JoftiException(
580: "Document not formatted correctly extra tag found "
581: + parser.getName());
582: }
583: } while (eventType != XmlPullParser.END_DOCUMENT);
584: parser.setInput(null);
585: } catch (Exception e) {
586:
587: throw new JoftiException(e);
588: } finally {
589:
590: if (stream != null) {
591: try {
592: stream.close();
593: } catch (Exception e) {
594: // we do not care about this
595: }
596: }
597: }
598: return config;
599: }
600:
601: }
|