KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > source > impl > XMLDBSource


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.source.impl;
17
18 import java.io.ByteArrayInputStream JavaDoc;
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.OutputStream JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24
25 import javax.xml.transform.TransformerFactory JavaDoc;
26 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
27 import javax.xml.transform.sax.TransformerHandler JavaDoc;
28 import javax.xml.transform.stream.StreamResult JavaDoc;
29
30 import org.apache.avalon.framework.context.Context;
31 import org.apache.avalon.framework.logger.AbstractLogEnabled;
32 import org.apache.avalon.framework.logger.Logger;
33 import org.apache.avalon.framework.service.ServiceManager;
34 import org.apache.avalon.framework.service.ServiceSelector;
35 import org.apache.cocoon.CascadingIOException;
36 import org.apache.cocoon.ProcessingException;
37 import org.apache.cocoon.ResourceNotFoundException;
38 import org.apache.cocoon.components.source.helpers.SourceCredential;
39 import org.apache.cocoon.serialization.Serializer;
40 import org.apache.cocoon.xml.IncludeXMLConsumer;
41 import org.apache.excalibur.source.ModifiableSource;
42 import org.apache.excalibur.source.Source;
43 import org.apache.excalibur.source.SourceException;
44 import org.apache.excalibur.source.SourceValidity;
45 import org.apache.excalibur.xml.sax.XMLizable;
46 import org.xml.sax.ContentHandler JavaDoc;
47 import org.xml.sax.SAXException JavaDoc;
48 import org.xml.sax.helpers.AttributesImpl JavaDoc;
49 import org.xmldb.api.DatabaseManager;
50 import org.xmldb.api.base.Collection;
51 import org.xmldb.api.base.Resource;
52 import org.xmldb.api.base.ResourceIterator;
53 import org.xmldb.api.base.ResourceSet;
54 import org.xmldb.api.base.XMLDBException;
55 import org.xmldb.api.modules.CollectionManagementService;
56 import org.xmldb.api.modules.XMLResource;
57 import org.xmldb.api.modules.XPathQueryService;
58
59 /**
60  * This class implements the xmldb:// pseudo-protocol and allows to get XML
61  * content from an XML:DB enabled XML database.
62  *
63  * @author <a HREF="mailto:gianugo@apache.org">Gianugo Rabellino</a>
64  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
65  * @version CVS $Id: XMLDBSource.java 265724 2005-09-01 13:41:08Z sylvain $
66  */

67 public class XMLDBSource extends AbstractLogEnabled
68     implements Source, ModifiableSource, XMLizable {
69
70     //
71
// Static Strings used for XML Collection representation
72
//
73

74     /** Source namespace */
75     public static final String JavaDoc URI = "http://apache.org/cocoon/xmldb/1.0";
76
77     /** Source prefix */
78     public static final String JavaDoc PREFIX = "db";
79
80     /** Root element <code>&lt;collections&gt;</code> */
81     protected static final String JavaDoc COLLECTIONS = "collections";
82     /** Root element <code>&lt;xmldb:collections&gt;</code> (raw name) */
83     protected static final String JavaDoc QCOLLECTIONS = PREFIX + ":" + COLLECTIONS;
84     /** Attribute <code>resources</code> on the root element indicates count of resources in the collection */
85     protected static final String JavaDoc RESOURCE_COUNT_ATTR = "resources";
86     /** Attribute <code>collections</code> on the root element indicates count of collections in the collection */
87     protected static final String JavaDoc COLLECTION_COUNT_ATTR = "collections";
88     protected static final String JavaDoc COLLECTION_BASE_ATTR = "base";
89
90     /** Element <code>&lt;collection&gt;</code> */
91     protected static final String JavaDoc COLLECTION = "collection";
92     /** Element <code>&lt;xmldb:collection&gt;</code> (raw name) */
93     protected static final String JavaDoc QCOLLECTION = PREFIX + ":" + COLLECTION;
94
95     /** Element <code>&lt;resource&gt;</code> */
96     protected static final String JavaDoc RESOURCE = "resource";
97     /** Element <code>&lt;resource&gt;</code> (raw name) */
98     protected static final String JavaDoc QRESOURCE = PREFIX + ":" + RESOURCE;
99     /** Attribute <code>name</code> on the collection/resource element */
100     protected static final String JavaDoc NAME_ATTR = "name";
101
102     /** Root element <code>&lt;results&gt;</code> */
103     protected static final String JavaDoc RESULTSET = "results";
104     /** Root element <code>&lt;xmldb:results&gt;</code> (raw name) */
105     protected static final String JavaDoc QRESULTSET = PREFIX + ":" + RESULTSET;
106     protected static final String JavaDoc QUERY_ATTR = "query";
107     protected static final String JavaDoc RESULTS_COUNT_ATTR = "resources";
108
109     /** Element <code>&lt;result&gt;</code> */
110     protected static final String JavaDoc RESULT = "result";
111     /** Element <code>&lt;xmldb:result&gt;</code> (raw name) */
112     protected static final String JavaDoc QRESULT = PREFIX + ":" + RESULT;
113     protected static final String JavaDoc RESULT_DOCID_ATTR = "docid";
114     protected static final String JavaDoc RESULT_ID_ATTR = "id";
115
116     protected static final String JavaDoc CDATA = "CDATA";
117
118     //
119
// Instance variables
120
//
121

122     /** The requested URL */
123     protected String JavaDoc url;
124
125     /** The supplied user */
126     protected String JavaDoc user;
127
128     /** The supplied password */
129     protected String JavaDoc password;
130
131     /** The part of URL after # sign */
132     protected String JavaDoc query;
133
134     /** The System ID */
135     protected String JavaDoc systemId;
136
137     /** ServiceManager */
138     protected final ServiceManager manager;
139
140     /** XMLDBOutputStream for writing to Modifiable resource */
141     protected XMLDBOutputStream os;
142
143     /** The Avalon context */
144     protected final Context context;
145
146     /**
147      * The constructor.
148      *
149      * @param logger the Logger instance.
150      * @param credential username and password
151      * @param url the URL being queried.
152      * @param manager component manager
153      */

154     public XMLDBSource(Logger logger,
155                        SourceCredential credential,
156                        String JavaDoc url,
157                        ServiceManager manager,
158                        Context context) {
159         enableLogging(logger);
160         this.context = context;
161         this.manager = manager;
162
163         this.user = credential.getPrincipal();
164         this.password = credential.getPassword();
165
166         // Parse URL
167
int start = url.indexOf('#');
168         if (start != -1) {
169             this.url = url.substring(0, start);
170             this.query = url.substring(start + 1);
171         } else {
172             this.url = url;
173         }
174         this.os = null;
175     }
176
177     /**
178      * Stream SAX events to a given ContentHandler. If the requested
179      * resource is a collection, build an XML view of it.
180      */

181     public void toSAX(ContentHandler JavaDoc handler) throws SAXException JavaDoc {
182         try {
183             if (url.endsWith("/")) {
184                 this.collectionToSAX(handler);
185             } else {
186                 this.resourceToSAX(handler);
187             }
188         } catch (ProcessingException pe) {
189             throw new SAXException JavaDoc("ProcessingException", pe);
190         }
191     }
192
193     private void resourceToSAX(ContentHandler JavaDoc handler)
194     throws SAXException JavaDoc, ProcessingException {
195
196         final String JavaDoc col = url.substring(0, url.lastIndexOf('/'));
197         final String JavaDoc res = url.substring(url.lastIndexOf('/') + 1);
198
199         Collection collection = null;
200         try {
201             collection = DatabaseManager.getCollection(col, user, password);
202             if (collection == null) {
203                 throw new ResourceNotFoundException("Document " + url + " not found");
204             }
205
206             XMLResource xmlResource = (XMLResource) collection.getResource(res);
207             if (xmlResource == null) {
208                 throw new ResourceNotFoundException("Document " + url + " not found");
209             }
210
211             if (query != null) {
212                 // Query resource
213
if (getLogger().isDebugEnabled()) {
214                     getLogger().debug("Querying resource " + res + " from collection " + url + "; query= " + this.query);
215                 }
216
217                 queryToSAX(handler, collection, res);
218             } else {
219                 // Return entire resource
220
if (getLogger().isDebugEnabled()) {
221                     getLogger().debug("Obtaining resource " + res + " from collection " + col);
222                 }
223
224                 xmlResource.getContentAsSAX(handler);
225             }
226         } catch (XMLDBException xde) {
227             String JavaDoc error = "Unable to fetch content. Error "
228                            + xde.errorCode + ": " + xde.getMessage();
229             throw new SAXException JavaDoc(error, xde);
230         } finally {
231             if (collection != null) {
232                 try {
233                     collection.close();
234                 } catch (XMLDBException ignored) {
235                 }
236             }
237         }
238     }
239
240     private void collectionToSAX(ContentHandler JavaDoc handler)
241     throws SAXException JavaDoc, ProcessingException {
242
243         AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
244
245         Collection collection = null;
246         try {
247             collection = DatabaseManager.getCollection(url, user, password);
248             if (collection == null) {
249                 throw new ResourceNotFoundException("Collection " + url +
250                                                     " not found");
251             }
252
253             if (query != null) {
254                 // Query collection
255
if (getLogger().isDebugEnabled()) {
256                     getLogger().debug("Querying collection " + url + "; query= " + this.query);
257                 }
258
259                 queryToSAX(handler, collection, null);
260             } else {
261                 // List collection
262
if (getLogger().isDebugEnabled()) {
263                     getLogger().debug("Listing collection " + url);
264                 }
265
266                 final String JavaDoc nresources = Integer.toString(collection.getResourceCount());
267                 attributes.addAttribute("", RESOURCE_COUNT_ATTR,
268                                         RESOURCE_COUNT_ATTR, "CDATA", nresources);
269                 final String JavaDoc ncollections = Integer.toString(collection.getChildCollectionCount());
270                 attributes.addAttribute("", COLLECTION_COUNT_ATTR,
271                                         COLLECTION_COUNT_ATTR, "CDATA", ncollections);
272                 attributes.addAttribute("", COLLECTION_BASE_ATTR,
273                                         COLLECTION_BASE_ATTR, "CDATA", url);
274
275                 handler.startDocument();
276                 handler.startPrefixMapping(PREFIX, URI);
277                 handler.startElement(URI, COLLECTIONS, QCOLLECTIONS, attributes);
278
279                 // Print child collections
280
String JavaDoc[] collections = collection.listChildCollections();
281                 for (int i = 0; i < collections.length; i++) {
282                     attributes.clear();
283                     attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, collections[i]);
284                     handler.startElement(URI, COLLECTION, QCOLLECTION, attributes);
285                     handler.endElement(URI, COLLECTION, QCOLLECTION);
286                 }
287
288                 // Print child resources
289
String JavaDoc[] resources = collection.listResources();
290                 for (int i = 0; i < resources.length; i++) {
291                     attributes.clear();
292                     attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, resources[i]);
293                     handler.startElement(URI, RESOURCE, QRESOURCE, attributes);
294                     handler.endElement(URI, RESOURCE, QRESOURCE);
295                 }
296
297                 handler.endElement(URI, COLLECTIONS, QCOLLECTIONS);
298                 handler.endPrefixMapping(PREFIX);
299                 handler.endDocument();
300             }
301         } catch (XMLDBException xde) {
302             String JavaDoc error = "Collection listing failed. Error " + xde.errorCode + ": " + xde.getMessage();
303             throw new SAXException JavaDoc(error, xde);
304         } finally {
305             if (collection != null) {
306                 try {
307                     collection.close();
308                 } catch (XMLDBException ignored) {
309                 }
310             }
311         }
312     }
313
314     private void queryToSAX(ContentHandler JavaDoc handler, Collection collection, String JavaDoc resource)
315     throws SAXException JavaDoc {
316
317         AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
318
319         try {
320             XPathQueryService service =
321                     (XPathQueryService) collection.getService("XPathQueryService", "1.0");
322             ResourceSet resultSet = (resource == null) ?
323                     service.query(query) : service.queryResource(resource, query);
324
325             attributes.addAttribute("", QUERY_ATTR, QUERY_ATTR, "CDATA", query);
326             attributes.addAttribute("", RESULTS_COUNT_ATTR,
327                                     RESULTS_COUNT_ATTR, "CDATA", Long.toString(resultSet.getSize()));
328
329             handler.startDocument();
330             handler.startPrefixMapping(PREFIX, URI);
331             handler.startElement(URI, RESULTSET, QRESULTSET, attributes);
332
333             IncludeXMLConsumer includeHandler = new IncludeXMLConsumer(handler);
334
335             // Print search results
336
ResourceIterator results = resultSet.getIterator();
337             while (results.hasMoreResources()) {
338                 XMLResource result = (XMLResource)results.nextResource();
339
340                 final String JavaDoc id = result.getId();
341                 final String JavaDoc documentId = result.getDocumentId();
342
343                 attributes.clear();
344                 if (id != null) {
345                     attributes.addAttribute("", RESULT_ID_ATTR, RESULT_ID_ATTR,
346                                             CDATA, id);
347                 }
348                 if (documentId != null) {
349                     attributes.addAttribute("", RESULT_DOCID_ATTR, RESULT_DOCID_ATTR,
350                                             CDATA, documentId);
351                 }
352
353                 handler.startElement(URI, RESULT, QRESULT, attributes);
354                 result.getContentAsSAX(includeHandler);
355                 handler.endElement(URI, RESULT, QRESULT);
356             }
357
358             handler.endElement(URI, RESULTSET, QRESULTSET);
359             handler.endPrefixMapping(PREFIX);
360             handler.endDocument();
361         } catch (XMLDBException xde) {
362             String JavaDoc error = "Query failed. Error " + xde.errorCode + ": " + xde.getMessage();
363             throw new SAXException JavaDoc(error, xde);
364         }
365     }
366
367     public void recycle() {
368         this.url = null;
369         this.user = null;
370         this.password = null;
371         this.query = null;
372     }
373
374     public String JavaDoc getURI() {
375         return url;
376     }
377
378     public long getContentLength() {
379         return -1;
380     }
381
382     public long getLastModified() {
383         return 0;
384     }
385
386     public boolean exists() {
387         final String JavaDoc col = url.substring(0, url.lastIndexOf('/'));
388         final String JavaDoc res = url.substring(url.lastIndexOf('/') + 1);
389         boolean result = true;
390
391         /* Ignore the query: we're just testing if the document exists. */
392         if (getLogger().isDebugEnabled()) {
393             getLogger().debug("Testing existence of resource `" + res + "' from collection `" + url + "'; query (ignored) = `" + this.query + "'");
394         }
395
396         Collection collection = null;
397         try {
398             collection = DatabaseManager.getCollection(col, user, password);
399             if (collection == null) {
400                 result = false;
401             } else {
402                 XMLResource xmlResource = (XMLResource) collection.getResource(res);
403                 if (xmlResource == null) {
404                     result = false;
405                 }
406             }
407         } catch (XMLDBException xde) {
408             result = false;
409         } finally {
410             if (collection != null) {
411                 try {
412                     collection.close();
413                 } catch (XMLDBException ignored) {
414                 }
415             }
416         }
417
418         return result;
419     }
420
421     public String JavaDoc getMimeType() {
422         return null;
423     }
424
425     public String JavaDoc getScheme() {
426         return url.substring(url.indexOf('/') - 1);
427     }
428
429     public SourceValidity getValidity() {
430         return null;
431     }
432
433     public void refresh() {
434     }
435
436     /**
437      * Get an InputSource for the given URL.
438      */

439     public InputStream JavaDoc getInputStream()
440     throws IOException JavaDoc {
441
442         ServiceSelector serializerSelector = null;
443         Serializer serializer = null;
444         // this.manager does not have Serializer
445
try {
446             TransformerFactory JavaDoc tf = TransformerFactory.newInstance();
447             TransformerHandler JavaDoc th =
448                 ((SAXTransformerFactory JavaDoc) tf).newTransformerHandler();
449             ByteArrayOutputStream JavaDoc bOut = new ByteArrayOutputStream JavaDoc();
450             StreamResult JavaDoc result = new StreamResult JavaDoc(bOut);
451             th.setResult(result);
452
453             toSAX(th);
454
455             return new ByteArrayInputStream JavaDoc(bOut.toByteArray());
456         } catch (Exception JavaDoc e) {
457             throw new CascadingIOException("Exception during processing of " + getURI(), e);
458         } finally {
459             if (serializer != null) {
460                 serializerSelector.release(serializer);
461             }
462             if (serializerSelector != null) {
463                 manager.release(serializerSelector);
464             }
465         }
466     }
467
468     /**
469      * Return an {@link OutputStream} to write to.
470      */

471     public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc, MalformedURLException JavaDoc {
472         if (query != null) {
473             throw new MalformedURLException JavaDoc("Cannot modify a resource that includes an XPATH expression");
474         }
475         this.os = new XMLDBOutputStream();
476         return this.os;
477     }
478
479     private void writeOutputStream(String JavaDoc content) throws SourceException {
480         String JavaDoc name = null;
481         String JavaDoc base = null;
482
483         try {
484             if (this.url.endsWith("/")) {
485                 name = "";
486                 base = this.url.substring(0, this.url.length() - 1);
487             } else {
488                 base = this.url.substring(0, this.url.lastIndexOf("/"));
489                 name = this.url.substring(this.url.lastIndexOf("/")+1);
490             }
491             Collection collection = DatabaseManager.getCollection(base, user, password);
492
493             if (name.equals("")) {
494                 name = collection.createId();
495                 this.url += name;
496             }
497             Resource resource = collection.createResource(name, "XMLResource");
498
499             resource.setContent(content);
500             collection.storeResource(resource);
501
502             getLogger().debug("Written to resource " + name);
503         } catch (XMLDBException e) {
504             String JavaDoc message = "Failed to create resource " + name + ": " + e.errorCode;
505             getLogger().debug(message, e);
506             throw new SourceException(message);
507         }
508     }
509
510     /**
511      * Delete the source
512      */

513     public void delete() throws SourceException {
514         String JavaDoc base = null;
515         String JavaDoc name = null;
516         if (this.url.endsWith("/")) {
517             try {
518                 // Cut trailing '/'
519
String JavaDoc k = this.url.substring(0, this.url.length() - 1);
520
521                 base = k.substring(0, k.lastIndexOf("/"));
522                 name = k.substring(k.lastIndexOf("/")+1);
523
524                 Collection collection = DatabaseManager.getCollection(base, user, password);
525
526                 CollectionManagementService service =
527                         (CollectionManagementService) collection.getService("CollectionManagementService", "1.0");
528                 service.removeCollection(name);
529             } catch (XMLDBException e) {
530                 String JavaDoc message = "Failed to remove collection " + name + ": " + e.errorCode;
531                 getLogger().error(message, e);
532                 throw new SourceException(message);
533             }
534         } else {
535             try {
536                 base = this.url.substring(0, this.url.lastIndexOf("/"));
537                 name = this.url.substring(this.url.lastIndexOf("/")+1);
538
539                 Collection collection = DatabaseManager.getCollection(base, user, password);
540
541                 Resource resource = collection.getResource(name);
542                 if (resource == null) {
543                     String JavaDoc message = "Resource " + name + " does not exist";
544                     getLogger().debug(message);
545                     throw new SourceException(message);
546                 } else {
547                     collection.removeResource(resource);
548                     getLogger().debug("Removed resource: "+ name);
549                 }
550             } catch (XMLDBException e) {
551                 String JavaDoc message = "Failed to delete resource " + name + ": " + e.errorCode;
552                 getLogger().debug(message, e);
553                 throw new SourceException(message);
554             }
555         }
556     }
557
558     /**
559      * Can the data sent to an <code>OutputStream</code> returned by
560      * {@link #getOutputStream()} be cancelled ?
561      *
562      * @return true if the stream can be cancelled
563      */

564     public boolean canCancel(OutputStream JavaDoc stream) {
565         return !this.os.isClosed();
566     }
567
568     /**
569      * Cancel the data sent to an <code>OutputStream</code> returned by
570      * {@link #getOutputStream()}.
571      *
572      * <p>After cancelling, the stream should no longer be used.</p>
573      */

574     public void cancel(OutputStream JavaDoc stream) throws IOException JavaDoc {
575         this.os.cancel();
576         this.os = null;
577     }
578
579     public class XMLDBOutputStream extends OutputStream JavaDoc {
580
581         private ByteArrayOutputStream JavaDoc baos;
582         private boolean isClosed;
583         public XMLDBOutputStream() {
584             baos = new ByteArrayOutputStream JavaDoc();
585             isClosed = false;
586         }
587
588         public void write(int b) throws IOException JavaDoc {
589             baos.write(b);
590         }
591
592         public void write(byte b[]) throws IOException JavaDoc {
593             baos.write(b);
594         }
595
596         public void write(byte b[], int off, int len) throws IOException JavaDoc {
597             baos.write(b, off, len);
598         }
599
600         public void close() throws IOException JavaDoc, SourceException {
601             if (!isClosed) {
602                 writeOutputStream(baos.toString());
603                 baos.close();
604                 this.isClosed = true;
605             }
606         }
607
608         public void flush() throws IOException JavaDoc {
609         }
610
611         public int size() {
612             return baos.size();
613         }
614
615         public boolean isClosed() {
616             return this.isClosed;
617         }
618         public void cancel() {
619             this.isClosed = true;
620         }
621     }
622 }
623
Popular Tags