001: package org.objectweb.celtix.configuration.impl;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.util.logging.Level;
006: import java.util.logging.Logger;
007:
008: import javax.xml.XMLConstants;
009: import javax.xml.namespace.QName;
010: import javax.xml.parsers.DocumentBuilder;
011: import javax.xml.parsers.DocumentBuilderFactory;
012: import javax.xml.parsers.ParserConfigurationException;
013: import javax.xml.transform.Source;
014: import javax.xml.transform.dom.DOMSource;
015: import javax.xml.transform.stream.StreamSource;
016: import javax.xml.validation.Schema;
017: import javax.xml.validation.SchemaFactory;
018: import javax.xml.validation.Validator;
019:
020: import org.w3c.dom.Document;
021: import org.w3c.dom.Element;
022: import org.w3c.dom.Node;
023:
024: import org.xml.sax.ErrorHandler;
025: import org.xml.sax.InputSource;
026: import org.xml.sax.SAXException;
027: import org.xml.sax.SAXParseException;
028:
029: import org.objectweb.celtix.common.i18n.Message;
030: import org.objectweb.celtix.common.logging.LogUtils;
031: import org.objectweb.celtix.configuration.ConfigurationException;
032: import org.objectweb.celtix.configuration.ConfigurationItemMetadata.LifecyclePolicy;
033: import org.objectweb.celtix.configuration.ConfigurationMetadata;
034: import org.objectweb.celtix.resource.DefaultResourceManager;
035:
036: public class ConfigurationMetadataBuilder {
037:
038: final class ValidatorErrorHandler implements ErrorHandler {
039:
040: public void error(SAXParseException exception)
041: throws SAXException {
042: throw exception;
043: }
044:
045: public void fatalError(SAXParseException exception)
046: throws SAXException {
047: throw exception;
048: }
049:
050: public void warning(SAXParseException exception)
051: throws SAXException {
052: throw exception;
053: }
054: }
055:
056: private static final Logger LOG = LogUtils
057: .getL7dLogger(ConfigurationMetadataBuilder.class);
058: private static final String MEATADATA_NAMESPACE_URI = "http://celtix.objectweb.org/configuration/metadata";
059: private static Schema metadataSchema;
060: private static Validator metadataValidator;
061:
062: private static ErrorHandler validatorErrorHandler;
063:
064: private final ConfigurationMetadataImpl model;
065: private final boolean forceDefaults;
066: private boolean doValidate;
067:
068: public ConfigurationMetadataBuilder(boolean fd) {
069: model = new ConfigurationMetadataImpl();
070: forceDefaults = fd;
071: }
072:
073: public void setValidation(boolean onOff) {
074: doValidate = onOff;
075: }
076:
077: public ConfigurationMetadata build(InputSource is)
078: throws IOException {
079: parseXML(is);
080: return model;
081: }
082:
083: public ConfigurationMetadata build(InputStream is)
084: throws IOException {
085: return build(new InputSource(is));
086: }
087:
088: private void deserializeConfig(Document document) {
089: Element root = document.getDocumentElement();
090: model.setNamespaceURI(root.getAttribute("namespace"));
091: model.setParentNamespaceURI(root
092: .getAttribute("parentNamespace"));
093: }
094:
095: private void deserializeConfigItem(Document document,
096: Element configItemElement) {
097:
098: ConfigurationItemMetadataImpl item = new ConfigurationItemMetadataImpl();
099:
100: for (Node nd = configItemElement.getFirstChild(); nd != null; nd = nd
101: .getNextSibling()) {
102: if (Node.ELEMENT_NODE != nd.getNodeType()) {
103: continue;
104: } else if ("name".equals(nd.getLocalName())) {
105: item.setName(ConfigurationMetadataUtils
106: .getElementValue(nd));
107: } else if ("type".equals(nd.getLocalName())) {
108: QName type = ConfigurationMetadataUtils
109: .elementValueToQName(document, (Element) nd);
110: item.setType(type);
111: if (doValidate) {
112: if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(type
113: .getNamespaceURI())) {
114: continue;
115: }
116: TypeSchema ts = new TypeSchemaHelper(forceDefaults)
117: .get(type.getNamespaceURI());
118: if (ts == null) {
119: throw new ConfigurationException(new Message(
120: "NO_TYPESCHEMA_FOR_NAMESPACE_EXC", LOG,
121: type.getNamespaceURI()));
122: }
123: if (!ts.hasType(type.getLocalPart())) {
124: throw new ConfigurationException(new Message(
125: "TYPE_NOT_DEFINED_IN_NAMESPACE_EXC",
126: LOG, type.getLocalPart(), type
127: .getNamespaceURI()));
128: }
129: }
130: } else if ("description".equals(nd.getLocalName())) {
131: // item.setDescription(getElementValue(nd));
132: } else if ("lifecyclePolicy".equals(nd.getLocalName())) {
133: String value = ConfigurationMetadataUtils
134: .getElementValue(nd);
135: if (null != value) {
136: if ("static".equals(value)) {
137: item.setLifecyclePolicy(LifecyclePolicy.STATIC);
138: } else if ("process".equals(value)) {
139: item
140: .setLifecyclePolicy(LifecyclePolicy.PROCESS);
141: } else if ("bus".equals(value)) {
142: item.setLifecyclePolicy(LifecyclePolicy.BUS);
143: } else {
144: item
145: .setLifecyclePolicy(LifecyclePolicy.DYNAMIC);
146: }
147: }
148: } else {
149: // this must be the extension element holding the default value
150: deserializeDefaultValue(item, (Element) nd);
151: }
152: }
153:
154: model.addItem(item);
155: }
156:
157: private void deserializeConfigItems(Document document) {
158: for (Node nd = document.getDocumentElement().getFirstChild(); nd != null; nd = nd
159: .getNextSibling()) {
160: if (Node.ELEMENT_NODE == nd.getNodeType()
161: && "configItem".equals(nd.getLocalName())
162: && MEATADATA_NAMESPACE_URI.equals(nd
163: .getNamespaceURI())) {
164: Element configItemElement = (Element) nd;
165: deserializeConfigItem(document, configItemElement);
166: }
167: }
168: }
169:
170: private void deserializeDefaultValue(
171: ConfigurationItemMetadataImpl item, Element data) {
172: /*
173: String namespaceURI = data.getNamespaceURI();
174: System.out.println("deserializeDefaultValue: \n"
175: + " data namespaceURI: " + namespaceURI + "\n"
176: + " data localName: " + data.getLocalName() + "\n"
177: + " item type: " + item.getType());
178:
179: if (!namespaceURI.equals(item.getType().getNamespaceURI())) {
180: Message msg = new Message("INVALID_ELEMENT_FOR_DEFAULT_VALUE_EXC",
181: LOG, item.getName(), item.getType());
182: throw new ConfigurationException(msg);
183: }
184: TypeSchema ts = new TypeSchemaHelper().get(namespaceURI);
185: assert ts != null;
186: String name = data.getLocalName();
187: QName type = ts.getDeclaredType(name);
188: if (null == type || !type.equals(item.getType().getLocalPart())) {
189: Message msg = new Message("INVALID_ELEMENT_FOR_DEFAULT_VALUE_EXC",
190: LOG, item.getName(), item.getType());
191: throw new ConfigurationException(msg);
192: }
193: unmarshalDefaultValue(item, data);
194: */
195: String elementName = data.getLocalName();
196: String namespaceURI = data.getNamespaceURI();
197: TypeSchema ts = new TypeSchemaHelper(forceDefaults)
198: .get(namespaceURI);
199: QName type = null;
200: if (null != ts) {
201: type = ts.getDeclaredType(elementName);
202: }
203: if (null == ts || null == type) {
204: System.err.println(elementName);
205: System.err.println(namespaceURI);
206: System.err.println(ts);
207: System.err.println(type);
208: throw new ConfigurationException(new Message(
209: "INVALID_ELEMENT_FOR_DEFAULT_VALUE_EXC", LOG, item
210: .getName(), item.getType()));
211: }
212: if (!type.equals(item.getType())) {
213: throw new ConfigurationException(new Message(
214: "INVALID_TYPE_FOR_DEFAULT_VALUE_EXC", LOG, item
215: .getName(), item.getType()));
216: }
217: unmarshalDefaultValue(item, data);
218: }
219:
220: private void deserializeImports(Document document) {
221: TypeSchemaHelper tsh = new TypeSchemaHelper(forceDefaults);
222: for (Node nd = document.getDocumentElement().getFirstChild(); nd != null; nd = nd
223: .getNextSibling()) {
224: if (Node.ELEMENT_NODE == nd.getNodeType()
225: && "configImport".equals(nd.getLocalName())
226: && MEATADATA_NAMESPACE_URI.equals(nd
227: .getNamespaceURI())) {
228: Element importElement = (Element) nd;
229: String location = importElement
230: .getAttribute("location");
231: String namespaceURI = importElement
232: .getAttribute("namespace");
233: if (null == tsh.get(namespaceURI)) {
234: tsh.get(namespaceURI, document.getDocumentURI(),
235: location);
236: }
237: }
238: }
239: }
240:
241: /**
242: * The configuration metadata schema is obtained system resource
243: * "schemas/configuration/metadata.xsd".
244: * It requires that either the resources directory is on the classpath or that
245: * the resources is listed in the classpath specified in the manifest of celtix.jar.
246: *
247: * @return the metadata schema
248: */
249:
250: private Schema getMetadataSchema() {
251: if (null == metadataSchema) {
252: InputStream is = DefaultResourceManager.instance()
253: .getResourceAsStream(
254: "schemas/configuration/metadata.xsd");
255:
256: if (null == is) {
257: throw new ConfigurationException(new Message(
258: "CANNOT_FIND_CONFIG_METADATA_SCHEMA_MSG", LOG));
259: }
260:
261: try {
262: metadataSchema = getSchema(is);
263: } catch (ConfigurationException ex) {
264: // should never happen as metadata schema is immutable
265: LOG.log(Level.SEVERE,
266: "CANNOT_CREATE_CONFIG_METADATA_SCHEMA_MSG", ex);
267: }
268: }
269: return metadataSchema;
270: }
271:
272: private Validator getMetadataValidator() {
273: if (null == metadataValidator) {
274: Schema schema = getMetadataSchema();
275: // assert null != schema;
276: metadataValidator = schema.newValidator();
277: if (null == validatorErrorHandler) {
278: validatorErrorHandler = new ValidatorErrorHandler();
279: }
280: metadataValidator.setErrorHandler(validatorErrorHandler);
281: // assert null != metadataValidator;
282: }
283: return metadataValidator;
284: }
285:
286: private Schema getSchema(InputStream is) {
287: Source schemaFile = new StreamSource(is);
288:
289: SchemaFactory factory = SchemaFactory
290: .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
291: Schema schema = null;
292: try {
293: schema = factory.newSchema(schemaFile);
294: } catch (SAXException ex) {
295: throw new ConfigurationException(new Message(
296: "SCHEMA_CREATION_ERROR_EXC", LOG), ex);
297: }
298: return schema;
299: }
300:
301: private void parseXML(InputSource is) throws IOException {
302:
303: // parse
304: Document document = null;
305: try {
306: DocumentBuilderFactory factory = DocumentBuilderFactory
307: .newInstance();
308: factory.setNamespaceAware(true);
309: DocumentBuilder parser = factory.newDocumentBuilder();
310: document = parser.parse(is);
311: } catch (ParserConfigurationException ex) {
312: throw new ConfigurationException(new Message(
313: "PARSER_CONFIGURATION_ERROR_EXC", LOG), ex);
314: } catch (SAXException ex) {
315: throw new ConfigurationException(new Message(
316: "PARSE_ERROR_EXC", LOG), ex);
317: }
318:
319: if (doValidate) {
320: try {
321: Validator v = getMetadataValidator();
322: v.validate(new DOMSource(document));
323: } catch (SAXException ex) {
324: Message msg = new Message(
325: "METADATA_VALIDATION_ERROR_EXC", LOG);
326: throw new ConfigurationException(msg, ex);
327: }
328: }
329:
330: deserializeImports(document);
331: deserializeConfig(document);
332: deserializeConfigItems(document);
333: }
334:
335: private void unmarshalDefaultValue(
336: ConfigurationItemMetadataImpl item, Element data) {
337: TypeSchema ts = new TypeSchemaHelper(forceDefaults).get(data
338: .getNamespaceURI());
339: Object obj = ts.unmarshalDefaultValue(item, data, doValidate);
340: if (null != obj) {
341: item.setDefaultValue(obj);
342: }
343: }
344: }
|