001: package org.mandarax.zkb;
002:
003: /*
004: * Copyright (C) 1999-2004 <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</a>
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020:
021: import java.io.*;
022: import java.net.URL;
023: import java.util.Properties;
024: import org.apache.log4j.Category;
025: import org.jdom.Document;
026: import org.jdom.JDOMException;
027: import org.jdom.input.SAXBuilder;
028: import org.jdom.output.XMLOutputter;
029: import org.mandarax.kernel.KnowledgeBase;
030: import org.mandarax.util.logging.LogCategories;
031: import org.mandarax.zkb.framework.ZKBDriver_2_0;
032:
033: /**
034: * Persistency manager supporting the serialization of knowledge bases. The
035: * functionality is similar to the XBKManager. However, there are the following
036: * differences:
037: * <ol>
038: * <li>The ZKB Manager serializes the knowledge base as XML, similiar to XKB.
039: * It uses modified XKB drivers to achieve this.
040: * <li>Objects (in particular objects used in constant terms) are not XML
041: * serialized with the knowledge base. Instead, they are referenced by (string)
042: * uris.
043: * <li>Objects are serialized using a separate
044: * <em>ObjectPersistncyService (OPS)</em>. The idea is to re-use existing
045: * services such as binary and (JDK-) XML serialization.
046: * <li>The two streams (knowledge base and objects (resources)) are zipped
047: * together,
048: * </ol>
049: * <br>
050: * Public methods are synchronized in order to ensure the consistency of the OPS
051: * used (added in 3.2).
052: *
053: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich </A>
054: * @version 3.4 <7 March 05>
055: * @since 2.2
056: */
057: public class ZKBManager implements LogCategories {
058:
059: // logging
060: public static final Category LOG_ZKB = Category
061: .getInstance("LOG_MANDARAX_ZKB");
062:
063: // meta property keys
064: public static final String ZKB_DRIVER_CLASS = "zkb-driver-class";
065: public static final String OPS = "ops-class";
066: public static final String ATTACHMENT = "attachment-uri";
067: private ObjectPersistencyService ops = new XMLSerializationOPS();
068: private ZKBDriver driver = new ZKBDriver_2_0();
069: private boolean usingSAX = false;
070: private SAXBuilder saxBuilder = null;
071: private boolean validate = false;
072: private static IOManager DIR_IO = new DirectoryIOManager();
073: private static IOManager ZIP_IO = new ZIPIOManager();
074:
075: /**
076: * Set the SAX builder. E.g., here we can set a special XML parser. Note
077: * that validation of the builder and validation set here could become
078: * inconsistent!
079: * @param builder a SAX builder
080: */
081: public synchronized void setSAXBuilder(SAXBuilder builder) {
082: saxBuilder = builder;
083: }
084:
085: /**
086: * Constructor.
087: */
088: public ZKBManager() {
089: super ();
090: }
091:
092: /**
093: * Exports a knowledge base to an output stream.
094: * @param target the output (a file or output stream)
095: * @param kb a knowledge base
096: * @throws a ZKBException is thrown if export fails
097: */
098: public synchronized void exportKnowledgeBase(Object target,
099: KnowledgeBase kb) throws ZKBException {
100: exportKnowledgeBase(target, kb, null);
101: }
102:
103: /**
104: * Exports a knowledge base to an output stream.
105: * @param target the output (a file or output stream)
106: * @param kb a knowledge base
107: * @param attachment an attachment
108: * @throws a ZKBException is thrown if export fails
109: */
110: public synchronized void exportKnowledgeBase(Object target,
111: KnowledgeBase kb, Object attachment) throws ZKBException {
112:
113: IOManager ioMgr = this .getIOManager(target);
114: LOG_ZKB.debug("Using the following IO Manager to export kb: "
115: + ioMgr);
116:
117: // write all data first into a memory buffer and decide later whether to
118: // zip them
119: // or store them as different files in a folder
120: byte[] metaData, kbData, resourceData;
121: ByteArrayOutputStream out = new ByteArrayOutputStream();
122:
123: // 1. prepare kb
124: Document doc = driver.exportKnowledgeBase(kb, ops);
125:
126: try {
127:
128: // 2. build manifest
129: Properties metadata = new Properties();
130: metadata.setProperty(OPS, this .ops.getClass().getName());
131: metadata.setProperty(ZKB_DRIVER_CLASS, driver.getClass()
132: .getName());
133:
134: // 3. export attachment
135: if (attachment != null) {
136: String attachmentUri = ops.findOrBind(attachment);
137: metadata.setProperty(ATTACHMENT, attachmentUri);
138: }
139: metadata.store(out, "mandarax zkb meta data");
140: out.close();
141: metaData = out.toByteArray();
142:
143: // 4. export resources
144: out = new ByteArrayOutputStream();
145: ops.exportObjects(out);
146: out.close();
147: resourceData = out.toByteArray();
148:
149: // 5. export kb
150: out = new ByteArrayOutputStream();
151: XMLOutputter xmlOut = new XMLOutputter(" ", true);
152: xmlOut.output(doc, out);
153: out.close();
154: kbData = out.toByteArray();
155:
156: // 6. write data
157: ioMgr.write(target, metaData, kbData, resourceData, ops);
158: } catch (Exception x) {
159: throw new ZKBException(x.getMessage(), x);
160: } finally {
161: ops.reset();
162: }
163: }
164:
165: /**
166: * Import a knowledge base.
167: * @return a knowledge base
168: * @param source an input stream, file or url
169: * @throws a ZKBException is thrown if import fails
170: */
171: public synchronized KnowledgeBase importKnowledgeBase(Object source)
172: throws ZKBException {
173: return importKnowledgeBaseAndAttachment(source).getKB();
174: }
175:
176: /**
177: * Import a knowledge base.
178: * @return a knowledge base plus an attachment
179: * @param source an input stream, file or url
180: * @throws a ZKBException is thrown if import fails
181: */
182: public synchronized KnowledgeBasePlusAttachment importKnowledgeBaseAndAttachment(
183: Object source) throws ZKBException {
184: IOManager ioMgr = this .getIOManager(source);
185: LOG_ZKB.debug("Using the following IO Manager to import kb: "
186: + ioMgr);
187:
188: IOManager.Data data = null;
189:
190: try {
191: data = ioMgr.read(source);
192: } catch (Exception x) {
193: error("Cannot read zkb", x);
194: }
195:
196: // build xkb driver
197: ZKBDriver importZKB = null;
198: String zkbDriverClassName = data.metaData
199: .getProperty(ZKB_DRIVER_CLASS);
200: try {
201: LOG_ZKB.debug("Using ZKB driver : " + zkbDriverClassName);
202: importZKB = (ZKBDriver) Class.forName(zkbDriverClassName)
203: .newInstance();
204: } catch (ClassNotFoundException x) {
205: error("Cannot find class " + zkbDriverClassName, x);
206: } catch (InstantiationException x) {
207: error("Cannot instanciate " + zkbDriverClassName, x);
208: } catch (IllegalAccessException x) {
209: error("Cannot access class " + zkbDriverClassName, x);
210: }
211:
212: // read objects
213: try {
214: data.ops.importObjects(new ByteArrayInputStream(
215: data.resourceData));
216: } catch (IOException x) {
217: LOG_ZKB.error("Cannot read resources from zkb", x);
218: throw new ZKBException(x.getMessage(), x);
219: }
220:
221: // read kb
222: KnowledgeBase kb = null;
223: try {
224: kb = importZKB.importKnowledgeBase(
225: getDocument(new ByteArrayInputStream(data.kbData)),
226: data.ops);
227:
228: // attachment
229: Object attachment = null;
230: String attachmentUri = data.metaData
231: .getProperty(ATTACHMENT);
232: if (attachmentUri != null)
233: attachment = data.ops.lookup(attachmentUri);
234:
235: return new KnowledgeBasePlusAttachment(kb, attachment);
236: } catch (Exception x) {
237: error("Cannot read knowledge from zkb ", x);
238: }
239: return null;
240:
241: }
242:
243: /**
244: * Returns the object persistency service.
245: *
246: * @return the object persistency service
247: */
248: public synchronized ObjectPersistencyService getOps() {
249: return ops;
250: }
251:
252: /**
253: * Sets the object persistency service.
254: *
255: * @param ops
256: * The ops to set
257: */
258: public synchronized void setOps(ObjectPersistencyService ops) {
259: this .ops = ops;
260: }
261:
262: /**
263: * Returns the driver.
264: *
265: * @return ZKBDriver
266: */
267: public synchronized ZKBDriver getDriver() {
268: return driver;
269: }
270:
271: /**
272: * Sets the driver.
273: *
274: * @param driver
275: * The driver to set
276: */
277: public synchronized void setDriver(ZKBDriver driver) {
278: this .driver = driver;
279: }
280:
281: /**
282: * Get the document from the data sorce.
283: *
284: * @return a document
285: * @param InputStream
286: * the data source
287: */
288: private Document getDocument(InputStream data) throws ZKBException {
289: if (data == null) {
290: throw new ZKBException("Cannot build document from null");
291: }
292: try {
293: return getSAXBuilder().build(data);
294: } catch (JDOMException x) {
295: throw new ZKBException(x.getMessage(), x);
296: } catch (IOException x) {
297: throw new ZKBException(x.getMessage(), x);
298: }
299: }
300:
301: /**
302: * Get the SAX builder.
303: *
304: * @return the SAX builder
305: */
306: private SAXBuilder getSAXBuilder() {
307: if (saxBuilder == null) {
308: saxBuilder = new SAXBuilder(validate);
309: }
310:
311: return saxBuilder;
312: }
313:
314: /**
315: * Log and (re-)throw an exception.
316: *
317: * @param msg
318: * the exception message
319: * @param x
320: * the cause
321: */
322: private void error(String msg, Exception x) throws ZKBException {
323: LOG_ZKB.error(msg);
324: throw new ZKBException(msg, x);
325: }
326:
327: /**
328: * Log and (re-)throw an exception.
329: *
330: * @param msg
331: * the exception message
332: */
333: private void error(String msg) throws ZKBException {
334: error(msg, null);
335: }
336:
337: /**
338: * Get the IO Manager used to access a target or source.
339: * @param object the target or source
340: * @return the IOManager used
341: */
342: private IOManager getIOManager(Object object) {
343: if (object instanceof File) {
344: File f = (File) object;
345: if (f.isDirectory())
346: return DIR_IO;
347: else
348: return ZIP_IO;
349: } else if (object instanceof InputStream)
350: return ZIP_IO;
351: else if (object instanceof URL)
352: return ZIP_IO;
353: return null;
354: }
355:
356: }
|