001: package com.quadcap.server;
002:
003: /* Copyright 2000 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.BufferedInputStream;
042: import java.io.BufferedReader;
043: import java.io.FileInputStream;
044: import java.io.FileReader;
045: import java.io.IOException;
046:
047: import java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.Comparator;
050: import java.util.HashMap;
051: import java.util.Properties;
052:
053: import org.xml.sax.AttributeList;
054: import org.xml.sax.DocumentHandler;
055: import org.xml.sax.ErrorHandler;
056: import org.xml.sax.InputSource;
057: import org.xml.sax.Parser;
058: import org.xml.sax.Locator;
059: import org.xml.sax.SAXException;
060: import org.xml.sax.SAXParseException;
061:
062: import org.xml.sax.helpers.ParserFactory;
063:
064: import com.quadcap.util.ConfigString;
065: import com.quadcap.util.Debug;
066:
067: /*{com.quadcap.server.ServerConfigParser.xml}
068: *
069: * <el><name>server</name>
070: * <p>This document is used to configure and initialize QED services.</p>
071: *
072: * <el><name>service</name>
073: * <p>Each service definition has the following allowed elements.</p>
074: *
075: * <el><name>service-name</name>
076: * <p>The name of the service.</p>
077: * </el>
078: *
079: * <el><name>service-class</name>
080: * <p>The name of the Java class which implements the service.</p>
081: * </el>
082: *
083: * <el><name>service-config</name>
084: * <p>The name of a service configuration file.</p>
085: * </el>
086: *
087: * <el><name>load-on-startup</name>
088: * <p>An integer value used to control the order in which
089: * services are loaded. Lower values are started first.</p>
090: * </el>
091: *
092: * <el><name>init-parameter</name>
093: * <p>String-valued configuration parameters may be supplied to
094: * a service.</p>
095: *
096: * <el><name>param-name</name>
097: * <p>The initialization/configuration parameter name.</p>
098: * </el>
099: *
100: * <el><name>param-value</name>
101: * <p>The initialization/configuration parameter value.</p>
102: * </el>
103: * </el>
104: * </el>
105: * </el>
106: */
107:
108: /**
109: * Parser the <code>server.xml</code> file to determine which services
110: * should be loaded on startup.
111: *
112: * @author Stan Bailes
113: */
114: public class ServerConfigParser implements DocumentHandler,
115: ErrorHandler {
116: ServiceContainer container;
117: Parser parser;
118: Locator locator;
119: StringBuffer data = new StringBuffer();
120: ArrayList services = new ArrayList();
121: ServiceWrapper service = null;
122: Properties props = null;
123: String paramName = null;
124: String paramValue = null;
125:
126: static final int INIT = 0;
127: static final int SERVER = 1;
128: static final int SERVICE = 2;
129: static final int SERVICE_NAME = 3;
130: static final int SERVICE_CLASS = 4;
131: static final int SERVICE_CONFIG = 5;
132: static final int LOAD_ON_STARTUP = 6;
133: static final int INIT_PARAMETER = 7;
134: static final int PARAM_NAME = 8;
135: static final int PARAM_VALUE = 9;
136:
137: static HashMap elemMap = new HashMap();
138: static {
139: elemMap.put("server", new Integer(SERVER));
140: elemMap.put("service", new Integer(SERVICE));
141: elemMap.put("service-name", new Integer(SERVICE_NAME));
142: elemMap.put("service-class", new Integer(SERVICE_CLASS));
143: elemMap.put("service-config", new Integer(SERVICE_CONFIG));
144: elemMap.put("load-on-startup", new Integer(LOAD_ON_STARTUP));
145: elemMap.put("init-parameter", new Integer(INIT_PARAMETER));
146: elemMap.put("param-name", new Integer(PARAM_NAME));
147: elemMap.put("param-value", new Integer(PARAM_VALUE));
148: }
149:
150: static final int mapElement(String name) {
151: Integer i = (Integer) elemMap.get(name);
152: if (i == null)
153: return -1;
154: return i.intValue();
155: }
156:
157: /**
158: * No-argument constructor. The new object needs a database connection
159: * before it can do anything useful.
160: *
161: * @exception Exception may be thrown if there's a problem constructing
162: * the XML parser.
163: */
164: public ServerConfigParser(ServiceContainer c) throws Exception {
165: this .container = c;
166: ConfigString ps = ConfigString.find("xml.sax.parser",
167: "com.quadcap.text.sax.Parser");
168:
169: try {
170: ClassLoader cl = ClassLoader.getSystemClassLoader();
171: this .parser = (Parser) (Class.forName(ps.toString(), true,
172: cl).newInstance());
173: } catch (Throwable t) {
174: Debug.print(t);
175: }
176: parser.setDocumentHandler(this );
177: parser.setErrorHandler(this );
178: }
179:
180: /**
181: * Parse the specified file
182: *
183: * @exception may be thrown
184: */
185: public void parse(String fileName) throws Exception {
186: FileReader f = new FileReader(fileName);
187: try {
188: BufferedReader r = new BufferedReader(f);
189: InputSource is = new InputSource(r);
190: parser.parse(is);
191: } finally {
192: f.close();
193: }
194: }
195:
196: /**
197: * SAX parser callback to handle XML Parser errors.
198: * This implementation just prints them
199: * to System.err.
200: *
201: * @param exception the exception generated by the parser.
202: */
203: public void error(SAXParseException exception) {
204: System.err.println("error");
205: exception.printStackTrace(System.err);
206: }
207:
208: /**
209: * SAX parser callback to handle XML Parser fatal errors.
210: * This implementation just prints them
211: * to System.err.
212: *
213: * @param exception the exception generated by the parser.
214: */
215: public void fatalError(SAXParseException exception) {
216: System.err.println("fatal error");
217: exception.printStackTrace(System.err);
218: }
219:
220: /**
221: * SAX parser callback to handle XML Parser fatal errors.
222: * This implementation just prints them
223: * to System.err.
224: *
225: * @param exception the exception generated by the parser.
226: */
227: public void warning(SAXParseException exception) {
228: System.err.println("warning");
229: exception.printStackTrace(System.err);
230: }
231:
232: /**
233: * SAX parser callback to handle character data found in the
234: * parsed document.
235: *
236: * @param ch the buffer containing the parsed characters.
237: * @param star the buffer position of the first character
238: * @param length the number of characters
239: *
240: * @exception SAXException may be thrown if this data represents
241: * a database value and there's a SQL exception thrown while
242: * trying to update the underlying resultset object with this
243: * data.
244: */
245: public void characters(char[] ch, int start, int length)
246: throws SAXException {
247: data.append(ch, start, length);
248: }
249:
250: /**
251: * SAX parser callback function that is called when the end of the
252: * document is reached. This implementation does nothing.
253: */
254: public void endDocument() {
255: }
256:
257: /**
258: * SAX parser callback function called for the end of an element.
259: * If this element
260: * represents the <code><database></code> element, we finish up
261: * by closing the active statement. If this element represents a table
262: * row element, we insert the current row. Otherwise, we do nothing.
263: *
264: * @param name the name of this element
265: * @exception SAXException may be thrown to wrap any underlying database
266: * exception.
267: */
268: public void endElement(String name) throws SAXException {
269: switch (mapElement(name)) {
270: case SERVER:
271: addServices();
272: break;
273: case SERVICE:
274: if (props != null) {
275: service.setServiceProperties(props);
276: props = null;
277: }
278: services.add(service);
279: break;
280: case SERVICE_NAME:
281: service.setServiceName(consume());
282: break;
283: case SERVICE_CLASS:
284: service.setServiceClass(consume());
285: break;
286: case LOAD_ON_STARTUP:
287: service.setLoadOnStartup(Integer.parseInt(consume()));
288: break;
289: case SERVICE_CONFIG: {
290: String cname = consume();
291: getProps().setProperty("service.config", cname);
292: try {
293: getProps().putAll(parseProps(cname));
294: } catch (IOException ex) {
295: }
296: }
297: break;
298: case PARAM_NAME:
299: paramName = consume();
300: break;
301: case PARAM_VALUE:
302: paramValue = consume();
303: break;
304: case INIT_PARAMETER:
305: if (paramName != null && paramValue != null) {
306: getProps().put(paramName, paramValue);
307: paramName = null;
308: paramValue = null;
309: }
310: break;
311: default:
312: throw new SAXException("endElement: bad element: " + name);
313: }
314: }
315:
316: final private Properties getProps() {
317: if (props == null)
318: props = new Properties();
319: return props;
320: }
321:
322: final public static Properties parseProps(String fileName)
323: throws IOException {
324: FileInputStream f = new FileInputStream(fileName);
325: Properties p = new Properties();
326: try {
327: BufferedInputStream b = new BufferedInputStream(f);
328: p.load(b);
329: } finally {
330: f.close();
331: }
332: return p;
333: }
334:
335: /**
336: * SAX parser callback for ignorable whitespace. We just ignore it
337: *
338: * @param ch the buffer containing the parsed characters.
339: * @param star the buffer position of the first character
340: * @param length the number of characters
341: */
342: public void ignorableWhitespace(char[] ch, int start, int length) {
343: }
344:
345: /**
346: * SAX parser callback for processing instructions. This implementation
347: * does nothing.
348: *
349: * @param target the processing instruction target.
350: * @param data the processing instruction data.
351: */
352: public void processingInstruction(String target, String data) {
353: }
354:
355: /**
356: * SAX parser callback used to receive a document locator.
357: *
358: * @param locator the parser's locator object.
359: */
360: public void setDocumentLocator(Locator locator) {
361: this .locator = locator;
362: }
363:
364: /**
365: * SAX parser callback for document start.
366: * This implementation does nothing.
367: */
368: public void startDocument() {
369: }
370:
371: /**
372: * SAX parser callback for the start of an element. If this element
373: * represents a table row, and the table is different from the last
374: * table seen, we establish an updatable <code>ResultSet</code> for the
375: * new table which can be used to insert new rows into the table.
376: * If this element represents a table row, we move to the insert row.
377: * If this element represents a column, we remember the column name.
378: *
379: * @param name the element name
380: * @param attrs the element's attributes
381: *
382: * @exception SAXException may be thrown to wrap an underlying database
383: * error, or if there is a problem with the XML document itself.
384: */
385: public void startElement(String name, AttributeList attrs)
386: throws SAXException {
387: data.setLength(0);
388: switch (mapElement(name)) {
389: case SERVICE:
390: service = new ServiceWrapper();
391: break;
392: default:
393: break;
394: case -1:
395: throw new SAXException("Unrecognized element: " + name);
396: }
397: }
398:
399: /**
400: * Get and return all of the accumulated character data as a String.
401: * Reset the character data buffer to be empty.
402: */
403: final String consume() {
404: String s = data.toString().trim();
405: data.setLength(0);
406: return s;
407: }
408:
409: /**
410: * At the end of the file, add the services in the specified order
411: */
412: final void addServices() throws SAXException {
413: Exception ex = null;
414:
415: Collections.sort(services, new Comparator() {
416: public int compare(Object a, Object b) {
417: ServiceWrapper wa = (ServiceWrapper) a;
418: ServiceWrapper wb = (ServiceWrapper) b;
419: if (wa.getLoadOnStartup() < wb.getLoadOnStartup())
420: return -1;
421: if (wa.getLoadOnStartup() > wb.getLoadOnStartup())
422: return 1;
423: return 0;
424: }
425: });
426: for (int i = 0; i < services.size(); i++) {
427: ServiceWrapper w = (ServiceWrapper) services.get(i);
428: try {
429: container.addService(w.getServiceName(), w
430: .getServiceClass(), w.getServiceProperties());
431: } catch (Exception e) {
432: ex = e;
433: Debug.print(e);
434: }
435: }
436:
437: if (ex != null)
438: throw new SAXException(ex);
439: }
440: }
|