001: // You can redistribute this software and/or modify it under the terms of
002: // the Ozone Library License version 1 published by ozone-db.org.
003: //
004: // The original code and portions created by SMB are
005: // Copyright (C) 1997-@year@ by SMB GmbH. All rights reserved.
006: //
007: // $Id: XMLContainer.java,v 1.1 2001/12/18 11:03:24 per_nyfelt Exp $
008:
009: package org.ozoneDB.xml.util;
010:
011: import java.io.IOException;
012: import java.io.ObjectOutput;
013: import java.io.ObjectInput;
014: import java.io.Externalizable;
015:
016: import org.ozoneDB.*;
017:
018: import org.w3c.dom.Document;
019: import org.w3c.dom.Node;
020: import org.w3c.dom.NodeList;
021:
022: import org.xml.sax.InputSource;
023: import org.xml.sax.ContentHandler;
024:
025: import org.infozone.tools.xml.queries.XObject;
026: import org.infozone.tools.xml.queries.XPathQuery;
027: import org.infozone.tools.xml.queries.XUpdateQuery;
028:
029: import org.ozoneDB.xml.dom.DocumentProxy;
030:
031: /**
032: * <p>This class is the central part of the ozone/XML API. Basically it provides a
033: * persistent container for a XML document, which is stored in an ozone database.</p>
034: *
035: * <p><dl><dt>IMPORTANT:</dt><dd>Before calling one of the store or extract methods the
036: * thread <b>must</b> have joined a transaction.</dd></dl>
037: * </p>
038: *
039: * Usage of the SAX methods is recomended, because of the better performance.
040: *
041: * @version $Revision: 1.1 $ $Date: 2001/12/18 11:03:24 $
042: * @author <a href="http://www.smb-tec.com">SMB</a>
043: */
044: public class XMLContainer implements Externalizable,
045: SAXChunkProducerDelegate {
046:
047: // Class data
048:
049: public final static boolean debug = false;
050:
051: // Data
052:
053: private XMLContainerHelper helper;
054:
055: private transient OzoneInterface db;
056:
057: private transient Document doc;
058:
059: /** True if this object runs outside an ozone server. */
060: private transient boolean runsExternal;
061:
062: // Class methods
063:
064: /**
065: * Creates a new container with the given name.
066: * @param _db The database where the container will be stored.
067: * @param _docName The name of the container (and the document).
068: * @return the new container.
069: */
070: public static XMLContainer newContainer(OzoneInterface _db,
071: String _docName) throws Exception {
072: XMLContainerHelper helper = (XMLContainerHelper) _db
073: .createObject(
074: "org.ozoneDB.xml.util.XMLContainerHelperImpl",
075: OzoneInterface.Public, _docName);
076:
077: return new XMLContainer(_db, helper);
078: }
079:
080: /**
081: * Returns the XMLContainer representing the document with the given name.
082: * @param _db The database where the container is stored.
083: * @param _docName The name under which the container has been stored.
084: * @return the container for the given name or null if the container
085: * does not exist.
086: */
087: public static XMLContainer forName(OzoneInterface _db,
088: String _docName) throws Exception {
089: XMLContainerHelper helper = (XMLContainerHelper) _db
090: .objectForName(_docName);
091: return helper != null ? new XMLContainer(_db, helper) : null;
092: }
093:
094: /**
095: * Returns the XMLContainer representing the document the given node
096: * belongs to.
097: * @param _db The database where the container is stored.
098: * @param _pNode A node of the document the container represents.
099: * @return the container for the given node or null if the node has not
100: * been stored using the XMLContainer class.
101: */
102: public static XMLContainer forNode(OzoneInterface _db, Node _pNode)
103: throws Exception {
104: if (!(_pNode instanceof OzoneProxy)) {
105: throw new IllegalArgumentException(
106: "Not a persistent DOM node: "
107: + _pNode.getClass().getName());
108: }
109:
110: DocumentProxy pDoc = _pNode instanceof Document ? (DocumentProxy) _pNode
111: : (DocumentProxy) _pNode.getOwnerDocument();
112:
113: XMLContainerHelper helper = (XMLContainerHelper) pDoc
114: .getContainer();
115: return helper != null ? new XMLContainer(_db, helper) : null;
116: }
117:
118: // Constructors
119:
120: protected XMLContainer(OzoneInterface _db,
121: XMLContainerHelper _helper) {
122: db = _db;
123: helper = _helper;
124: runsExternal = (db instanceof ExternalDatabase);
125: }
126:
127: // Methods
128:
129: /**
130: * Changes the name of this container.
131: * @param _name The new name of this container or null to remove the
132: * current name.
133: */
134: public void setName(String _name) throws Exception {
135: if (helper == null) {
136: throw new IllegalStateException(
137: "Document has already been deleted.");
138: }
139: db.nameObject(helper, _name);
140: }
141:
142: /**
143: * Deletes the container (and the associated document) from the database.
144: */
145: public synchronized void delete() throws Exception {
146: if (helper == null) {
147: throw new IllegalStateException(
148: "Document has already been deleted.");
149: }
150: db.deleteObject(helper);
151: helper = null;
152: doc = null;
153: }
154:
155: /**
156: * Get the underlying persistent document that this container is working on.
157: * @return The persistent document.
158: */
159: public Document getPDocument() throws Exception {
160: if (helper == null) {
161: throw new IllegalStateException(
162: "Document has already been deleted.");
163: }
164: // not synchronized because it wouldn't make a difference here
165: if (doc == null) {
166: doc = helper.getDocument();
167: }
168: return doc;
169: }
170:
171: /**
172: * Stores a transient DOM tree database. The newly created Nodes are
173: * appended to the Document Node of this container.
174: *
175: * @see #storeDOM(Node, Node)
176: */
177: public void storeDOM(Document _tNode) throws Exception {
178: if (helper == null)
179: throw new IllegalStateException(
180: "Document has already been deleted.");
181:
182: storeDOM(null, _tNode);
183: }
184:
185: /**
186: * Stores a transient node into the database.
187: *
188: * <p>Before calling this method the current thread <b>must</b> have joined an
189: * {@link org.ozoneDB.ExternalTransaction explicit Transaction}. This is an
190: * exception to the normal case, where an implicit transaction (handled by
191: * Ozone) is more appropriate.</p>
192: * <p><dl><dt>Note:</dt><dd>If ever possible the corresponding
193: * {@link #storeSAX(Node) SAX method} should be used, because the performance
194: * of SAX storage is much better.</dd></dl></p>
195: *
196: * @param _pNode The persistent node where the stored node will be appended to.
197: * Null replaces the current document.
198: * @param _tnode The transient node to be stored.
199: *
200: * @throws IllegalStateException if the underlying document has already been deleted.
201: * @throws IllegalArgumentException if _tnode was null.
202: * @throws IllegalStateException if the current thread has not joined a
203: * {@link org.ozoneDB.ExternalTransaction transaction}.
204: * @see org.ozoneDB.ExternalTransaction
205: * @see org.ozoneDB.ExternalDatabase#newTransaction()
206: */
207: public void storeDOM(Node _pNode, Node _tNode) throws Exception {
208: long time = 0;
209: long starttime = 0;
210:
211: if (helper == null) {
212: throw new IllegalStateException(
213: "Document has already been deleted.");
214: }
215: if (_tNode == null) {
216: throw new IllegalArgumentException("tNode == null.");
217: }
218: // thread must have joined a transaction
219: if (runsExternal
220: && ((ExternalDatabase) db).currentTransaction() == null) {
221: throw new IllegalStateException(
222: "Thread must have joined a transaction!");
223: }
224:
225: SAXChunkConsumer consumer = helper.beginInputSequence(_pNode);
226: ModifiableNodeList mnl = new ModifiableNodeList(1);
227: mnl.addNode(_tNode);
228: SAXChunkProducer producer = new SAXChunkProducer(mnl);
229: ChunkOutputStream cos = producer.chunkStream();
230:
231: do {
232: cos.reset();
233: producer.createNextChunk();
234: if (XMLContainer.debug) {
235: starttime = System.currentTimeMillis();
236: }
237: consumer = helper.putChunk(cos.toByteArray(), consumer);
238: if (XMLContainer.debug) {
239: time += (System.currentTimeMillis() - starttime);
240: }
241: } while (!cos.getEndFlag());
242:
243: if (XMLContainer.debug) {
244: System.out.println("DOM store: store in db time: " + time
245: + " ms");
246: }
247: }
248:
249: /**
250: * Stores a DOM tree represented by SAX events in the datasabase. The
251: * newly created Nodes are appended to the Document Node of this container.
252: *
253: * @see #storeSAX(Node)
254: */
255: public ContentHandler storeSAX() throws Exception {
256: if (helper == null) {
257: throw new IllegalStateException(
258: "Document has already been deleted.");
259: }
260: return storeSAX(null);
261: }
262:
263: /**
264: * Stores XML represented by SAX events into the database.
265: * <p>The entire storage process <b>must</b> be enclosed by an
266: * {@link org.ozoneDB.ExternalTransaction explicit Transaction}. This is an
267: * exception to the normal case, where an implicit transaction (handled by
268: * Ozone) is more appropriate. The storage process starts with the call to this
269: * method and ends with the last event send to the content handler. The SAX
270: * events must be properly terminated, i.e. there must be an end event for
271: * for every start event, otherwise the correct storage can't be guaranteed.</p>
272: *
273: * @param _pNode The persistent node where the stored data will be appended to.
274: *
275: * @return the content handler which stores all XML data it recieves into the
276: * database.
277: *
278: * @throws IllegalStateException if the underlying document has already been deleted.
279: * @throws IllegalStateException if the current thread has not joined a
280: * {@link org.ozoneDB.ExternalTransaction transaction}.
281: * @see org.ozoneDB.ExternalTransaction
282: * @see org.ozoneDB.ExternalDatabase#newTransaction()
283: */
284: public ContentHandler storeSAX(Node _pNode) throws Exception {
285: if (helper == null) {
286: throw new IllegalStateException(
287: "Document has already been deleted.");
288: }
289: // thread must have joined a transaction
290: if (runsExternal
291: && ((ExternalDatabase) db).currentTransaction() == null) {
292: throw new IllegalStateException(
293: "Thread must have joined a transaction!");
294: }
295:
296: // this eventually blocks if there is another thread storing something already
297: SAXChunkConsumer consumer = helper.beginInputSequence(_pNode);
298:
299: SAXChunkProducer producer = new SAXChunkProducer(this );
300:
301: producer.dbConsumer = consumer;
302: return producer;
303: }
304:
305: /**
306: * This method is for internal use only. <b>Don't call it directly.</b>
307: */
308: public void processChunk(SAXChunkProducer _producer)
309: throws Exception {
310: if (XMLContainer.debug) {
311: System.out.print("XMLContainer.processChunk()... ");
312: }
313:
314: ChunkOutputStream cos = _producer.chunkStream();
315: if (XMLContainer.debug) {
316: System.out.print("putChunk(" + cos.count + ")... ");
317: }
318:
319: _producer.dbConsumer = helper.putChunk(cos.toByteArray(),
320: _producer.dbConsumer);
321:
322: }
323:
324: /**
325: * @see #extractDOM(Document, Node, Node , int)
326: */
327: public Document extractDOM(Document _domFactory) throws Exception {
328: return (Document) extractDOM(_domFactory, (Node) null, null, -1);
329: }
330:
331: /**
332: * @see #extractDOM(Document, Node, Node , int)
333: */
334: public Node extractDOM(Document _domFactory, Node _pNode,
335: Node _appendTo) throws Exception {
336: return extractDOM(_domFactory, _pNode, _appendTo, -1);
337: }
338:
339: /**
340: * Extracts a given DOM node and all its descendants.
341: *
342: * <p>Before calling this method the current thread <b>must</b> have joined an
343: * {@link org.ozoneDB.ExternalTransaction explicit Transaction}. This is an
344: * exception to the normal case, where an implicit transaction (handled by
345: * Ozone) is more appropriate.</p>
346: *
347: * <p><dl><dt>Note:</dt><dd>If possible, the corresponding
348: * {@link #extractSAX(ContentHandler) SAX method} should be used, because the
349: * performance of SAX retrieval is much better.</dd></dl></p>
350: *
351: * @param _domFactory The DOM Document that is used to create the extracted DOM nodes.
352: * @param _pNode The persistent DOM node
353: * @param _appendTo The transient DOM node where the extracted content will
354: * will be appended to.
355: * @param _depth The number of hierarchy steps that should be extracted or
356: * null if the entire hierarchy should be extracted.
357: */
358: public Node extractDOM(Document _domFactory, Node _pNode,
359: Node _appendTo, int _depth) throws Exception {
360:
361: ModifiableNodeList mnl = null;
362: if (_pNode != null) {
363: mnl = new ModifiableNodeList(1);
364: mnl.addNode(_pNode);
365: }
366:
367: NodeList resultList = extractDOM(_domFactory, mnl, _appendTo,
368: _depth);
369:
370: return resultList.item(0);
371:
372: /* if (helper == null) {
373: throw new IllegalStateException ("Document has already been deleted.");
374: }
375: // thread must have joined a transaction
376: if (runsExternal && ((ExternalDatabase)db).currentTransaction() == null) {
377: throw new IllegalStateException( "Thread must have joined a transaction!" );
378: }
379:
380: SAXChunkProducer producer = helper.beginOutputSequence( _pNode, _depth );
381: SAXChunkConsumer consumer = new SAXChunkConsumer(_domFactory, _appendTo);
382: ChunkOutputStream cos;
383: do {
384: producer = helper.createNextChunk( producer );
385: cos = producer.chunkStream();
386: consumer.processChunk( cos.toByteArray() );
387: cos.reset();
388: } while (!cos.getEndFlag());
389:
390: return consumer.getResultNode();*/
391: }
392:
393: public NodeList extractDOM(Document _domFactory, NodeList _pNodes,
394: Node _appendTo, int _depth) throws Exception {
395: if (helper == null) {
396: throw new IllegalStateException(
397: "Document has already been deleted.");
398: }
399: // thread must have joined a transaction
400: if (runsExternal
401: && ((ExternalDatabase) db).currentTransaction() == null) {
402: throw new IllegalStateException(
403: "Thread must have joined a transaction!");
404: }
405:
406: SAXChunkProducer producer = helper.beginOutputSequence(_pNodes,
407: _depth);
408: SAXChunkConsumer consumer = new SAXChunkConsumer(_domFactory,
409: _appendTo);
410: ChunkOutputStream cos;
411: do {
412: producer = helper.createNextChunk(producer);
413: cos = producer.chunkStream();
414: consumer.processChunk(cos.toByteArray());
415: cos.reset();
416: } while (!cos.getEndFlag());
417:
418: return consumer.getResultNodeList();
419: }
420:
421: /**
422: * @see #extractSAX(ContentHandler, Node, int)
423: */
424: public void extractSAX(ContentHandler _contentHandler)
425: throws Exception {
426: extractSAX(_contentHandler, null, -1);
427: }
428:
429: /**
430: * @see #extractSAX(ContentHandler, Node , int )
431: */
432: public void extractSAX(ContentHandler _contentHandler, Node _pNode)
433: throws Exception {
434: extractSAX(_contentHandler, _pNode, -1);
435: }
436:
437: /**
438: * Extracts a given DOM node and all its descendants.
439: *
440: * <p>Before calling this method the current thread <b>must</b> have joined an
441: * {@link org.ozoneDB.ExternalTransaction explicit Transaction}. This is an
442: * exception to the normal case, where an implicit transaction (handled by
443: * Ozone) is more appropriate.</p>
444: *
445: * @param _contentHandler The ContentHandler that will receive the generated
446: * SAX events.
447: * @param _pNode The persistent DOM node that is the starting point, or null
448: * if the entire document should be extracted.
449: * @param _depth The number of hierarchy steps that should be extracted or
450: * null if the entire hierarchy should be extracted.
451: */
452: public void extractSAX(ContentHandler _contentHandler, Node _pNode,
453: int _depth) throws Exception {
454:
455: if (helper == null) {
456: throw new IllegalStateException(
457: "Document has already been deleted.");
458: }
459: // thread must have joined a transaction
460: if (runsExternal
461: && ((ExternalDatabase) db).currentTransaction() == null) {
462: throw new IllegalStateException(
463: "Thread must have joined a transaction!");
464: }
465:
466: ModifiableNodeList mnl = null;
467: if (_pNode != null) {
468: mnl = new ModifiableNodeList(1);
469: mnl.addNode(_pNode);
470: }
471:
472: SAXChunkProducer producer = helper.beginOutputSequence(mnl,
473: _depth);
474: SAXChunkConsumer consumer = new SAXChunkConsumer(
475: _contentHandler);
476: ChunkOutputStream cos;
477: do {
478: producer = helper.createNextChunk(producer);
479: cos = producer.chunkStream();
480: consumer.processChunk(cos.toByteArray());
481: cos.reset();
482: } while (!cos.getEndFlag());
483: }
484:
485: /**
486: * Create a new XUpdate query. XUpdate is a descriptive XML update
487: * language. XUpdate is the recommended way to change a document in the
488: * database. See <a href=http://www.xmldb.org>www.xmldb.org</a> for more
489: * information and some XUpdate documentation.
490: *
491: * @see OzoneXUpdateQuery
492: */
493: public OzoneXUpdateQuery newXUpdateQuery() {
494: return new OzoneXUpdateQuery(this );
495: }
496:
497: /**
498: * Create a new XPath query.
499: *
500: * @see OzoneXPathQuery
501: */
502: public OzoneXPathQuery newXPathQuery() {
503: return new OzoneXPathQuery(this );
504: }
505:
506: protected void executeXUpdate(OzoneXUpdateQuery _query)
507: throws Exception {
508: if (_query == null) {
509: throw new IllegalArgumentException("_query == null.");
510: }
511: helper.executeXUpdate(_query);
512: }
513:
514: protected XObject executeXPath(OzoneXPathQuery _query)
515: throws Exception {
516: if (_query == null) {
517: throw new IllegalArgumentException("_query == null.");
518: }
519: return helper.executeXPath(_query);
520: }
521:
522: /**
523: * Determines the absolute XPath for the given node.
524: *
525: * @param node The W3C DOM node whose XPath is to determine.
526: * @return The string representing the absolute XPath for this node.
527: */
528: public String xpathForNode(Node _pnode) {
529: if (helper == null) {
530: throw new IllegalStateException(
531: "Document has already been deleted.");
532: }
533: return helper.xpathForNode(_pnode);
534: }
535:
536: public void writeExternal(ObjectOutput _out) throws IOException {
537: if (runsExternal) {
538: throw new IllegalStateException(
539: "XMLContainer cannot be serialized outside an ozone server.");
540: }
541: _out.writeObject(helper);
542: }
543:
544: public void readExternal(ObjectInput _in) throws IOException,
545: ClassNotFoundException {
546: if (runsExternal) {
547: throw new IllegalStateException(
548: "XMLContainer cannot be deserialized outside an ozone server.");
549: }
550: helper = (XMLContainerHelper) _in.readObject();
551: db = org.ozoneDB.core.Env.currentEnv().database;
552: }
553:
554: }
|