001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.storage.util;
011:
012: import java.util.*;
013:
014: import org.w3c.dom.*;
015: import org.xml.sax.InputSource;
016:
017: import org.mmbase.storage.*;
018: import org.mmbase.util.xml.DocumentReader;
019: import org.mmbase.util.logging.*;
020:
021: /**
022: * @javadoc
023: * @author Pierre van Rooden
024: * @version $Id: StorageReader.java,v 1.16 2008/02/18 14:09:42 michiel Exp $
025: * @since MMBase-1.7
026: */
027: public class StorageReader<SM extends StorageManager> extends
028: DocumentReader {
029:
030: private static final Logger log = Logging
031: .getLoggerInstance(StorageReader.class);
032:
033: /** Public ID of the Storage DTD version 1.0 */
034: public static final String PUBLIC_ID_STORAGE_1_0 = "-//MMBase//DTD storage config 1.0//EN";
035: /** DTD resource filename of the Database DTD version 1.0 */
036: public static final String DTD_STORAGE_1_0 = "storage_1_0.dtd";
037:
038: /** Public ID of the most recent Database DTD */
039: public static final String PUBLIC_ID_STORAGE = PUBLIC_ID_STORAGE_1_0;
040: /** DTD resource filename of the most Database DTD */
041: public static final String DTD_STORAGE = DTD_STORAGE_1_0;
042:
043: static {
044: org.mmbase.util.XMLEntityResolver.registerPublicID(
045: PUBLIC_ID_STORAGE_1_0, DTD_STORAGE_1_0,
046: StorageReader.class);
047: }
048:
049: /**
050: * The factory for which the reader reads the document.
051: * The factory is used to verify whether the document is compatible, and is used to instantiate objects
052: * that depend on factory information (such as schemes)
053: */
054: protected StorageManagerFactory factory;
055:
056: /**
057: * Constructor.
058: *
059: * @param factory the factory for which to read the storage configuration
060: * @param source to the xml document.
061: * @since MMBase-1.7
062: */
063: public StorageReader(StorageManagerFactory factory,
064: InputSource source) {
065: super (source, DocumentReader.validate(), StorageReader.class);
066: this .factory = factory;
067: }
068:
069: /**
070: * Attempt to load a StorageManager class, using the classname as given in the configuration.
071: * The method verifies whether the instantiated class is of the correct version.
072: * @return the storage manager Class, or null if none was configured
073: * @throws StorageConfigurationException if the factory version did not match, or the class configured is invalid
074: */
075: public Class<SM> getStorageManagerClass()
076: throws StorageConfigurationException {
077: Element root = document.getDocumentElement();
078: if (factory != null) {
079: // verify if the storagemanagerfactory is of the correct class, and
080: // of the correct version
081: NodeList factoryTagList = root
082: .getElementsByTagName("storagemanagerfactory");
083: if (factoryTagList.getLength() > 0) {
084: Element factoryTag = (Element) factoryTagList.item(0);
085: try {
086: // obtain and check class
087: String factoryClassName = factoryTag
088: .getAttribute("classname");
089: Class<SM> factoryClass = (Class<SM>) Class
090: .forName(factoryClassName);
091: if (!factoryClass.isInstance(factory)) {
092: throw new StorageConfigurationException(
093: "StorageManager Configuration requires factory class '"
094: + factoryClassName + "'.");
095: }
096: // obtain and check version
097: String storageManagerFactoryVersion = factoryTag
098: .getAttribute("version");
099: if (storageManagerFactoryVersion != null) {
100: double version = Double
101: .parseDouble(storageManagerFactoryVersion);
102: if (version > factory.getVersion()) {
103: throw new StorageConfigurationException(
104: "StorageManager Configuration requires factory version '"
105: + version + ", found "
106: + factory.getVersion()
107: + ".");
108: }
109: }
110: } catch (NumberFormatException pe) {
111: throw new StorageConfigurationException(pe); // version is not an integer
112: } catch (ClassNotFoundException cnfe) {
113: throw new StorageConfigurationException(cnfe); // factory class not found
114: }
115: }
116: }
117: NodeList managerTagList = root
118: .getElementsByTagName("storagemanager");
119: if (managerTagList.getLength() > 0) {
120: Element managerTag = (Element) managerTagList.item(0);
121: String managerClassName = managerTag
122: .getAttribute("classname");
123: // intantiate storage manager and check version
124: try {
125: Class<SM> managerClass = (Class<SM>) Class
126: .forName(managerClassName);
127: StorageManager manager = managerClass.newInstance();
128: // obtain and check version
129: String storageManagerVersion = managerTag
130: .getAttribute("version");
131: if (storageManagerVersion != null) {
132: double version = Double
133: .parseDouble(storageManagerVersion);
134: if (version > manager.getVersion()) {
135: throw new StorageConfigurationException(
136: "StorageManager Configuration requires storage manager version '"
137: + version + ", found "
138: + manager.getVersion() + ".");
139: }
140: }
141: return managerClass;
142: } catch (NumberFormatException pe) {
143: throw new StorageConfigurationException(pe); // version is not an integer
144: } catch (ClassNotFoundException cnfe) {
145: throw new StorageConfigurationException(cnfe);
146: } catch (IllegalAccessException iae) {
147: throw new StorageConfigurationException(iae);
148: } catch (InstantiationException ie) {
149: throw new StorageConfigurationException(ie);
150: }
151: } else {
152: return null;
153: }
154: }
155:
156: /**
157: * Attempt to obtain a list of SearchQueryHandler classes, using the classname as given in the configuration.
158: *
159: *
160: * @return A List of Class objects, each being the SearchQueryHandler class, or an empty list if none was configured
161: * @throws StorageConfigurationException if the class configured is invalid
162: */
163: public List<Class<?>> getSearchQueryHandlerClasses()
164: throws StorageConfigurationException {
165: // override if otherwise specified
166: List<Class<?>> classes = new ArrayList<Class<?>>();
167: Element root = document.getDocumentElement();
168: NodeList handlerTagList = root
169: .getElementsByTagName("searchqueryhandler");
170: for (int i = 0; i < handlerTagList.getLength(); i++) {
171: Element handlerTag = (Element) handlerTagList.item(i);
172: String queryHandlerClassName = handlerTag
173: .getAttribute("classname");
174: // get class
175: try {
176: classes.add(Class.forName(queryHandlerClassName));
177: } catch (ClassNotFoundException cnfe) {
178: throw new StorageConfigurationException(cnfe);
179: }
180: }
181: return classes;
182: }
183:
184: /**
185: * Reads all attributes from the reader and returns them as a map.
186: * This include options, as well as the following special attributes:
187: * <ul>
188: * <li>option-disallowed-fields-case-sensitive : has the Boolean value TRUE if disallowed fields are case sensitive (default FALSE)</li>
189: * </ul>
190: * @return attributes as a map
191: */
192: public Map<String, Object> getAttributes() {
193: Map<String, Object> attributes = new HashMap<String, Object>();
194: Element root = document.getDocumentElement();
195: NodeList attributesTagList = root
196: .getElementsByTagName("attributes");
197: if (attributesTagList.getLength() > 0) {
198: Element attributesTag = (Element) attributesTagList.item(0);
199: NodeList attributeTagList = attributesTag
200: .getElementsByTagName("attribute");
201: for (int i = 0; i < attributeTagList.getLength(); i++) {
202: Element attributeTag = (Element) attributeTagList
203: .item(i);
204: String attributeName = attributeTag
205: .getAttribute("name");
206: // require an attribute name.
207: // if not given, skip the option.
208: if (attributeName != null) {
209: attributes.put(attributeName,
210: getNodeTextValue(attributeTag));
211: }
212: }
213: NodeList optionTagList = attributesTag
214: .getElementsByTagName("option");
215: for (int i = 0; i < optionTagList.getLength(); i++) {
216: Element optionTag = (Element) optionTagList.item(i);
217: // require an option name.
218: // if not given, skip the option.
219: String optionName = optionTag.getAttribute("name");
220: if (optionName != null) {
221: String optionValue = optionTag
222: .getAttribute("value");
223: Boolean value = Boolean.TRUE;
224: if (optionValue != null && !optionValue.equals("")) {
225: value = Boolean.valueOf(optionValue);
226: }
227: attributes.put(optionName, value);
228: }
229: }
230: NodeList schemeTagList = attributesTag
231: .getElementsByTagName("scheme");
232: for (int i = 0; i < schemeTagList.getLength(); i++) {
233: Element schemeTag = (Element) schemeTagList.item(i);
234: String schemeName = schemeTag.getAttribute("name");
235: // require a scheme name
236: // if not given, skip the option
237: if (schemeName != null) {
238: String pattern = getNodeTextValue(schemeTag);
239: if (pattern == null || pattern.equals("")) {
240: attributes.put(schemeName, null);
241: } else {
242: attributes.put(schemeName, new Scheme(factory,
243: getNodeTextValue(schemeTag)));
244: }
245: }
246: }
247: }
248: // system attributes
249: NodeList disallowedFieldsList = root
250: .getElementsByTagName("disallowed-fields");
251: if (disallowedFieldsList.getLength() > 0) {
252: Element disallowedFieldsTag = (Element) disallowedFieldsList
253: .item(0);
254: attributes.put(Attributes.DISALLOWED_FIELD_CASE_SENSITIVE,
255: Boolean.valueOf(disallowedFieldsTag
256: .getAttribute("case-sensitive")));
257: attributes.put(Attributes.ENFORCE_DISALLOWED_FIELDS,
258: Boolean.valueOf(disallowedFieldsTag
259: .getAttribute("enforce")));
260: }
261: return attributes;
262: }
263:
264: /**
265: * Returns all disallowed fields and their possible alternate values.
266: * The fields are returned as name-value pairs, where the disallowedfieldname is the key, and
267: * the alternate name is the value (null if no name is given).
268: * @return disallowed fields as a map
269: */
270: public Map<String, String> getDisallowedFields() {
271: Map<String, String> disallowedFields = new HashMap<String, String>();
272: Element root = document.getDocumentElement();
273: NodeList disallowedFieldsList = root
274: .getElementsByTagName("disallowed-fields");
275: if (disallowedFieldsList.getLength() > 0) {
276: Element disallowedFieldsTag = (Element) disallowedFieldsList
277: .item(0);
278: boolean casesensitive = Boolean.valueOf(
279: disallowedFieldsTag.getAttribute("case-sensitive"))
280: .booleanValue();
281: NodeList fieldTagList = disallowedFieldsTag
282: .getElementsByTagName("disallowed-field");
283: for (int i = 0; i < fieldTagList.getLength(); i++) {
284: Element fieldTag = (Element) fieldTagList.item(i);
285: String fieldName = fieldTag.getAttribute("name");
286: // require a field name.
287: // if not given, skip the option.
288: if (fieldName != null) {
289: if (!casesensitive)
290: fieldName = fieldName.toLowerCase();
291: String replacement = fieldTag
292: .getAttribute("replacement");
293: disallowedFields.put(fieldName, replacement);
294: }
295: }
296: }
297: return disallowedFields;
298: }
299:
300: /**
301: * Returns all type mappings.
302: * The mappings are returned in the order that they were given in the reader.
303: * Calling code should sort this list if they want to use TypoMapping fuzzy matching.
304: * @return a List of TypeMapping objects
305: */
306: public List<TypeMapping> getTypeMappings() {
307: List<TypeMapping> typeMappings = new ArrayList<TypeMapping>();
308: Element root = document.getDocumentElement();
309: NodeList typeMappingsTagList = root
310: .getElementsByTagName("type-mappings");
311: if (typeMappingsTagList.getLength() > 0) {
312: Element typeMappingsTag = (Element) typeMappingsTagList
313: .item(0);
314: NodeList typeMappingTagList = typeMappingsTag
315: .getElementsByTagName("type-mapping");
316: for (int i = 0; i < typeMappingTagList.getLength(); i++) {
317: Element typeMappingTag = (Element) typeMappingTagList
318: .item(i);
319: TypeMapping typeMapping = new TypeMapping();
320: typeMapping.name = typeMappingTag.getAttribute("name");
321: // require a type-mapping name (a MMBase type)
322: // if not given, skip the option.
323: if (typeMapping.name != null) {
324: // obtain min/max values for sizes
325: try {
326: typeMapping.minSize = Long
327: .parseLong(typeMappingTag
328: .getAttribute("min-size"));
329: } catch (NumberFormatException nfe) {
330: }
331: try {
332: typeMapping.maxSize = Long
333: .parseLong(typeMappingTag
334: .getAttribute("max-size"));
335: } catch (NumberFormatException nfe) {
336: }
337: // get the type to convert to
338: typeMapping.type = typeMappingTag
339: .getAttribute("type");
340: typeMappings.add(typeMapping);
341: if (typeMapping.name.equals("BYTE")) {
342: log
343: .warn("In "
344: + this
345: + " deprecated mapping for 'BYTE' is specified. This must be changed to 'BINARY'");
346: typeMapping.name = "BINARY";
347: }
348: }
349: }
350: }
351: return typeMappings;
352: }
353:
354: /**
355: * @since MMBase-1.8.5
356: */
357: public List<String> getStoreBinaryAsFileObjects() {
358: List<String> binaryAsFileObjects = new ArrayList<String>();
359: Element root = document.getDocumentElement();
360: NodeList binaryAsFileOjectsTagList = root
361: .getElementsByTagName("store-binary-as-file-objects");
362: if (binaryAsFileOjectsTagList.getLength() > 0) {
363: Element binaryAsFileObjectsTag = (Element) binaryAsFileOjectsTagList
364: .item(0);
365: NodeList binaryAsFileObjectTagList = binaryAsFileObjectsTag
366: .getElementsByTagName("store-binary-as-file-object");
367: for (int i = 0; i < binaryAsFileObjectTagList.getLength(); i++) {
368: Element binaryAsFileobjectTag = (Element) binaryAsFileObjectTagList
369: .item(i);
370: String objectName = binaryAsFileobjectTag
371: .getAttribute("name");
372: binaryAsFileObjects.add(objectName);
373: }
374: }
375: return binaryAsFileObjects;
376: }
377: }
|