cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sylv...@apache.org
Subject svn commit: r349156 - in /cocoon/branches/BRANCH_2_1_X: src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java status.xml
Date Sat, 26 Nov 2005 22:49:20 GMT
Author: sylvain
Date: Sat Nov 26 14:49:13 2005
New Revision: 349156

URL: http://svn.apache.org/viewcvs?rev=349156&view=rev
Log:
Refactor xmldb which is now traversable, and add createId() and getBinaryOutputStream()

Modified:
    cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java
    cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java
    cocoon/branches/BRANCH_2_1_X/status.xml

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java?rev=349156&r1=349155&r2=349156&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSource.java
Sat Nov 26 14:49:13 2005
@@ -21,26 +21,22 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.MalformedURLException;
+import java.util.ArrayList;
 
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 import javax.xml.transform.stream.StreamResult;
 
-import org.apache.avalon.framework.context.Context;
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
 import org.apache.avalon.framework.logger.Logger;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.avalon.framework.service.ServiceSelector;
 import org.apache.cocoon.CascadingIOException;
-import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.ResourceNotFoundException;
-import org.apache.cocoon.components.source.helpers.SourceCredential;
-import org.apache.cocoon.serialization.Serializer;
 import org.apache.cocoon.xml.IncludeXMLConsumer;
-import org.apache.excalibur.source.ModifiableSource;
+import org.apache.excalibur.source.ModifiableTraversableSource;
 import org.apache.excalibur.source.Source;
 import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.SourceNotFoundException;
+import org.apache.excalibur.source.SourceUtil;
 import org.apache.excalibur.source.SourceValidity;
 import org.apache.excalibur.xml.sax.XMLizable;
 import org.xml.sax.ContentHandler;
@@ -52,6 +48,7 @@
 import org.xmldb.api.base.ResourceIterator;
 import org.xmldb.api.base.ResourceSet;
 import org.xmldb.api.base.XMLDBException;
+import org.xmldb.api.modules.BinaryResource;
 import org.xmldb.api.modules.CollectionManagementService;
 import org.xmldb.api.modules.XMLResource;
 import org.xmldb.api.modules.XPathQueryService;
@@ -60,12 +57,10 @@
  * This class implements the xmldb:// pseudo-protocol and allows to get XML
  * content from an XML:DB enabled XML database.
  *
- * @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
- * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
  * @version CVS $Id$
  */
 public class XMLDBSource extends AbstractLogEnabled
-    implements Source, ModifiableSource, XMLizable {
+    implements ModifiableTraversableSource, XMLizable {
 
     //
     // Static Strings used for XML Collection representation
@@ -134,44 +129,135 @@
     /** The System ID */
     protected String systemId;
 
-    /** ServiceManager */
-    protected final ServiceManager manager;
+    /** The path for the collection (same as url if it's a collection) */
+    private final String colPath;
+    
+    /** The name of the resource in the collection (null if a collection) */
+    private String resName;
+    
+    private Collection collection;
+    
+    private Resource resource;
+    
+    private static final int ST_UNKNOWN = 0;
+    private static final int ST_COLLECTION = 1;
+    private static final int ST_RESOURCE = 2;
+    private static final int ST_NO_PARENT = 3;
+    private static final int ST_NO_RESOURCE = 4;
 
-    /** XMLDBOutputStream for writing to Modifiable resource */
-    protected XMLDBOutputStream os;
-
-    /** The Avalon context */
-    protected final Context context;
+    private int status = ST_UNKNOWN;
 
     /**
      * The constructor.
      *
      * @param logger the Logger instance.
      * @param credential username and password
-     * @param url the URL being queried.
+     * @param srcUrl the URL being queried.
      * @param manager component manager
      */
     public XMLDBSource(Logger logger,
-                       SourceCredential credential,
-                       String url,
-                       ServiceManager manager,
-                       Context context) {
+                       String user, String password,
+                       String srcUrl) {
         enableLogging(logger);
-        this.context = context;
-        this.manager = manager;
 
-        this.user = credential.getPrincipal();
-        this.password = credential.getPassword();
+        this.user = user;
+        this.password = password;
 
         // Parse URL
-        int start = url.indexOf('#');
+        int start = srcUrl.indexOf('#');
         if (start != -1) {
-            this.url = url.substring(0, start);
-            this.query = url.substring(start + 1);
+            this.url = srcUrl.substring(0, start);
+            this.query = srcUrl.substring(start + 1);
+            if (query.length() == 0) {
+                query = null;
+            }
         } else {
-            this.url = url;
+            this.url = srcUrl;
+        }
+
+        // Split path in collection and resource
+        if (this.url.endsWith("/")) {
+            this.url = this.url.substring(0, this.url.length() - 1);
+        }
+        int pos = this.url.lastIndexOf('/');
+        colPath = this.url.substring(0, pos);
+        resName = this.url.substring(pos + 1);
+    }
+    
+    private void setup() throws XMLDBException, SourceException {
+        status = ST_UNKNOWN;
+        try {
+            collection = DatabaseManager.getCollection(url, user, password);
+            if (collection != null) {
+                status = ST_COLLECTION;
+                return;
+            }
+            
+            // That may be a resource: get the parent collection
+            collection = DatabaseManager.getCollection(colPath, user, password);
+            if (collection == null) {
+                // Even parent is unknown
+                status = ST_NO_PARENT;
+            } else {
+                resource = collection.getResource(resName);
+                if (resource != null) {
+                    // A resource
+                    status = ST_RESOURCE;
+                } else {
+                    status = ST_NO_RESOURCE;
+                }
+            }
+        } finally {
+            if (status == ST_UNKNOWN) {
+                // Something went wrong: ensure any collection is closed
+                cleanup();
+            }
+        }
+    }
+    
+    private void cleanup() {
+        close(this.collection);
+    }
+
+    private Collection createCollection(String path) throws XMLDBException, SourceException
{
+        Collection coll = DatabaseManager.getCollection(path, this.user, this.password);
+        if (coll != null) {
+            return coll;
+        }
+        // Need to create the collection
+
+        // Remove any trailing '/'
+        if (path.endsWith("/")) {
+            path = path.substring(0, path.length() - 1);
+        }
+
+        int pos = path.lastIndexOf('/');
+        if (pos == -1) {
+            throw new SourceException("Invalid collection path " + path);
+        }
+        // Recurse
+        Collection parentColl = createCollection(path.substring(0, pos));
+        
+        // And create the child collection
+        CollectionManagementService mgtService = (CollectionManagementService)
+            parentColl.getService("CollectionManagementService", "1.0");
+        coll = mgtService.createCollection(path.substring(pos+1));
+
+        return coll;
+    }
+    
+    
+    /**
+     * Close an XMLDB collection, ignoring any exception
+     */
+    private void close(Collection coll) {
+        if (coll != null) {
+            try {
+                coll.close();
+            } catch (XMLDBException e) {
+                // ignore;
+            }
         }
-        this.os = null;
     }
 
     /**
@@ -180,195 +266,160 @@
      */
     public void toSAX(ContentHandler handler) throws SAXException {
         try {
-            if (url.endsWith("/")) {
+            setup();
+            if (status == ST_COLLECTION) {
                 this.collectionToSAX(handler);
-            } else {
+            } else if (status == ST_RESOURCE) {
                 this.resourceToSAX(handler);
+            } else {
+                throw new SourceNotFoundException(getURI());
             }
-        } catch (ProcessingException pe) {
-            throw new SAXException("ProcessingException", pe);
+        } catch (SAXException se) {
+            throw se;
+        } catch (Exception e) {
+            throw new SAXException("Error processing " + getURI(), e);
+        } finally {
+            cleanup();
         }
     }
-
+    
     private void resourceToSAX(ContentHandler handler)
-    throws SAXException, ProcessingException {
+        throws SAXException, XMLDBException {
 
-        final String col = url.substring(0, url.lastIndexOf('/'));
-        final String res = url.substring(url.lastIndexOf('/') + 1);
+        if (!(resource instanceof XMLResource)) {
+            throw new SAXException("Not an XML resource: " + getURI());
+        }
 
-        Collection collection = null;
-        try {
-            collection = DatabaseManager.getCollection(col, user, password);
-            if (collection == null) {
-                throw new ResourceNotFoundException("Document " + url + " not found");
+        if (query != null) {
+            // Query resource
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Querying resource " + resName + " from collection " +
url + "; query= " + this.query);
             }
 
-            XMLResource xmlResource = (XMLResource) collection.getResource(res);
-            if (xmlResource == null) {
-                throw new ResourceNotFoundException("Document " + url + " not found");
+            queryToSAX(handler, collection, resName);
+        } else {
+            // Return entire resource
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Obtaining resource " + resName + " from collection " +
colPath);
             }
 
-            if (query != null) {
-                // Query resource
-                if (getLogger().isDebugEnabled()) {
-                    getLogger().debug("Querying resource " + res + " from collection " +
url + "; query= " + this.query);
-                }
-
-                queryToSAX(handler, collection, res);
-            } else {
-                // Return entire resource
-                if (getLogger().isDebugEnabled()) {
-                    getLogger().debug("Obtaining resource " + res + " from collection " +
col);
-                }
-
-                xmlResource.getContentAsSAX(handler);
-            }
-        } catch (XMLDBException xde) {
-            String error = "Unable to fetch content. Error "
-                           + xde.errorCode + ": " + xde.getMessage();
-            throw new SAXException(error, xde);
-        } finally {
-            if (collection != null) {
-                try {
-                    collection.close();
-                } catch (XMLDBException ignored) {
-                }
-            }
+            ((XMLResource)resource).getContentAsSAX(handler);
         }
     }
 
     private void collectionToSAX(ContentHandler handler)
-    throws SAXException, ProcessingException {
+        throws SAXException, XMLDBException {
 
         AttributesImpl attributes = new AttributesImpl();
 
-        Collection collection = null;
-        try {
-            collection = DatabaseManager.getCollection(url, user, password);
-            if (collection == null) {
-                throw new ResourceNotFoundException("Collection " + url +
-                                                    " not found");
+        if (query != null) {
+            // Query collection
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Querying collection " + url + "; query= " + this.query);
             }
 
-            if (query != null) {
-                // Query collection
-                if (getLogger().isDebugEnabled()) {
-                    getLogger().debug("Querying collection " + url + "; query= " + this.query);
-                }
+            queryToSAX(handler, collection, null);
+        } else {
+            // List collection
+            if (getLogger().isDebugEnabled()) {
+                getLogger().debug("Listing collection " + url);
+            }
 
-                queryToSAX(handler, collection, null);
-            } else {
-                // List collection
-                if (getLogger().isDebugEnabled()) {
-                    getLogger().debug("Listing collection " + url);
-                }
+            final String nresources = Integer.toString(collection.getResourceCount());
+            attributes.addAttribute("", RESOURCE_COUNT_ATTR,
+                                    RESOURCE_COUNT_ATTR, "CDATA", nresources);
+            final String ncollections = Integer.toString(collection.getChildCollectionCount());
+            attributes.addAttribute("", COLLECTION_COUNT_ATTR,
+                                    COLLECTION_COUNT_ATTR, "CDATA", ncollections);
+            attributes.addAttribute("", COLLECTION_BASE_ATTR,
+                                    COLLECTION_BASE_ATTR, "CDATA", url);
 
-                final String nresources = Integer.toString(collection.getResourceCount());
-                attributes.addAttribute("", RESOURCE_COUNT_ATTR,
-                                        RESOURCE_COUNT_ATTR, "CDATA", nresources);
-                final String ncollections = Integer.toString(collection.getChildCollectionCount());
-                attributes.addAttribute("", COLLECTION_COUNT_ATTR,
-                                        COLLECTION_COUNT_ATTR, "CDATA", ncollections);
-                attributes.addAttribute("", COLLECTION_BASE_ATTR,
-                                        COLLECTION_BASE_ATTR, "CDATA", url);
-
-                handler.startDocument();
-                handler.startPrefixMapping(PREFIX, URI);
-                handler.startElement(URI, COLLECTIONS, QCOLLECTIONS, attributes);
-
-                // Print child collections
-                String[] collections = collection.listChildCollections();
-                for (int i = 0; i < collections.length; i++) {
-                    attributes.clear();
-                    attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, collections[i]);
-                    handler.startElement(URI, COLLECTION, QCOLLECTION, attributes);
-                    handler.endElement(URI, COLLECTION, QCOLLECTION);
-                }
+            handler.startDocument();
+            handler.startPrefixMapping(PREFIX, URI);
+            handler.startElement(URI, COLLECTIONS, QCOLLECTIONS, attributes);
 
-                // Print child resources
-                String[] resources = collection.listResources();
-                for (int i = 0; i < resources.length; i++) {
-                    attributes.clear();
-                    attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, resources[i]);
-                    handler.startElement(URI, RESOURCE, QRESOURCE, attributes);
-                    handler.endElement(URI, RESOURCE, QRESOURCE);
-                }
+            // Print child collections
+            String[] collections = collection.listChildCollections();
+            for (int i = 0; i < collections.length; i++) {
+                attributes.clear();
+                attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, collections[i]);
+                handler.startElement(URI, COLLECTION, QCOLLECTION, attributes);
+                handler.endElement(URI, COLLECTION, QCOLLECTION);
+            }
 
-                handler.endElement(URI, COLLECTIONS, QCOLLECTIONS);
-                handler.endPrefixMapping(PREFIX);
-                handler.endDocument();
-            }
-        } catch (XMLDBException xde) {
-            String error = "Collection listing failed. Error " + xde.errorCode + ": " + xde.getMessage();
-            throw new SAXException(error, xde);
-        } finally {
-            if (collection != null) {
-                try {
-                    collection.close();
-                } catch (XMLDBException ignored) {
-                }
+            // Print child resources
+            String[] resources = collection.listResources();
+            for (int i = 0; i < resources.length; i++) {
+                attributes.clear();
+                attributes.addAttribute("", NAME_ATTR, NAME_ATTR, CDATA, resources[i]);
+                handler.startElement(URI, RESOURCE, QRESOURCE, attributes);
+                handler.endElement(URI, RESOURCE, QRESOURCE);
             }
+
+            handler.endElement(URI, COLLECTIONS, QCOLLECTIONS);
+            handler.endPrefixMapping(PREFIX);
+            handler.endDocument();
         }
     }
 
     private void queryToSAX(ContentHandler handler, Collection collection, String resource)
-    throws SAXException {
+        throws SAXException, XMLDBException {
 
         AttributesImpl attributes = new AttributesImpl();
 
-        try {
-            XPathQueryService service =
-                    (XPathQueryService) collection.getService("XPathQueryService", "1.0");
-            ResourceSet resultSet = (resource == null) ?
-                    service.query(query) : service.queryResource(resource, query);
-
-            attributes.addAttribute("", QUERY_ATTR, QUERY_ATTR, "CDATA", query);
-            attributes.addAttribute("", RESULTS_COUNT_ATTR,
-                                    RESULTS_COUNT_ATTR, "CDATA", Long.toString(resultSet.getSize()));
+        XPathQueryService service =
+                (XPathQueryService) collection.getService("XPathQueryService", "1.0");
+        ResourceSet resultSet = (resource == null) ?
+                service.query(query) : service.queryResource(resource, query);
 
-            handler.startDocument();
-            handler.startPrefixMapping(PREFIX, URI);
-            handler.startElement(URI, RESULTSET, QRESULTSET, attributes);
+        attributes.addAttribute("", QUERY_ATTR, QUERY_ATTR, "CDATA", query);
+        attributes.addAttribute("", RESULTS_COUNT_ATTR,
+                                RESULTS_COUNT_ATTR, "CDATA", Long.toString(resultSet.getSize()));
 
-            IncludeXMLConsumer includeHandler = new IncludeXMLConsumer(handler);
+        handler.startDocument();
+        handler.startPrefixMapping(PREFIX, URI);
+        handler.startElement(URI, RESULTSET, QRESULTSET, attributes);
 
-            // Print search results
-            ResourceIterator results = resultSet.getIterator();
-            while (results.hasMoreResources()) {
-                XMLResource result = (XMLResource)results.nextResource();
+        IncludeXMLConsumer includeHandler = new IncludeXMLConsumer(handler);
 
-                final String id = result.getId();
-                final String documentId = result.getDocumentId();
+        // Print search results
+        ResourceIterator results = resultSet.getIterator();
+        while (results.hasMoreResources()) {
+            XMLResource result = (XMLResource)results.nextResource();
 
-                attributes.clear();
-                if (id != null) {
-                    attributes.addAttribute("", RESULT_ID_ATTR, RESULT_ID_ATTR,
-                                            CDATA, id);
-                }
-                if (documentId != null) {
-                    attributes.addAttribute("", RESULT_DOCID_ATTR, RESULT_DOCID_ATTR,
-                                            CDATA, documentId);
-                }
+            final String id = result.getId();
+            final String documentId = result.getDocumentId();
 
-                handler.startElement(URI, RESULT, QRESULT, attributes);
-                result.getContentAsSAX(includeHandler);
-                handler.endElement(URI, RESULT, QRESULT);
+            attributes.clear();
+            if (id != null) {
+                attributes.addAttribute("", RESULT_ID_ATTR, RESULT_ID_ATTR,
+                                        CDATA, id);
+            }
+            if (documentId != null) {
+                attributes.addAttribute("", RESULT_DOCID_ATTR, RESULT_DOCID_ATTR,
+                                        CDATA, documentId);
             }
 
-            handler.endElement(URI, RESULTSET, QRESULTSET);
-            handler.endPrefixMapping(PREFIX);
-            handler.endDocument();
-        } catch (XMLDBException xde) {
-            String error = "Query failed. Error " + xde.errorCode + ": " + xde.getMessage();
-            throw new SAXException(error, xde);
+            handler.startElement(URI, RESULT, QRESULT, attributes);
+            try {
+                result.getContentAsSAX(includeHandler);
+            } catch(XMLDBException xde) {
+                // That may be a text-only result
+                Object content = result.getContent();
+                if (content instanceof String) {
+                    String text = (String)content;
+                    handler.characters(text.toCharArray(), 0, text.length());
+                } else {
+                    // Cannot do better
+                    throw xde;
+                }
+            }
+            handler.endElement(URI, RESULT, QRESULT);
         }
-    }
 
-    public void recycle() {
-        this.url = null;
-        this.user = null;
-        this.password = null;
-        this.query = null;
+        handler.endElement(URI, RESULTSET, QRESULTSET);
+        handler.endPrefixMapping(PREFIX);
+        handler.endDocument();
     }
 
     public String getURI() {
@@ -384,38 +435,14 @@
     }
 
     public boolean exists() {
-        final String col = url.substring(0, url.lastIndexOf('/'));
-        final String res = url.substring(url.lastIndexOf('/') + 1);
-        boolean result = true;
-
-        /* Ignore the query: we're just testing if the document exists. */
-        if (getLogger().isDebugEnabled()) {
-            getLogger().debug("Testing existence of resource `" + res + "' from collection
`" + url + "'; query (ignored) = `" + this.query + "'");
-        }
-
-        Collection collection = null;
         try {
-            collection = DatabaseManager.getCollection(col, user, password);
-            if (collection == null) {
-                result = false;
-            } else {
-                XMLResource xmlResource = (XMLResource) collection.getResource(res);
-                if (xmlResource == null) {
-                    result = false;
-                }
-            }
-        } catch (XMLDBException xde) {
-            result = false;
+            setup();
+            return status == ST_COLLECTION || status == ST_RESOURCE;
+        } catch (Exception e) {
+            return false;
         } finally {
-            if (collection != null) {
-                try {
-                    collection.close();
-                } catch (XMLDBException ignored) {
-                }
-            }
+            cleanup();
         }
-
-        return result;
     }
 
     public String getMimeType() {
@@ -423,7 +450,7 @@
     }
 
     public String getScheme() {
-        return url.substring(url.indexOf('/') - 1);
+        return SourceUtil.getScheme(url);
     }
 
     public SourceValidity getValidity() {
@@ -438,72 +465,118 @@
      */
     public InputStream getInputStream()
     throws IOException {
-
-        ServiceSelector serializerSelector = null;
-        Serializer serializer = null;
-        // this.manager does not have Serializer
+        
         try {
-            TransformerFactory tf = TransformerFactory.newInstance();
-            TransformerHandler th =
-                ((SAXTransformerFactory) tf).newTransformerHandler();
-            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-            StreamResult result = new StreamResult(bOut);
-            th.setResult(result);
+            setup();
+            // Check if it's binary
+            if (resource instanceof BinaryResource) {
+                Object obj = resource.getContent();
+                if (obj == null) obj = new byte[0];
+                if (obj instanceof byte[]) {
+                    return new ByteArrayInputStream((byte[])obj);
+                } else {
+                    throw new SourceException("Binary resource has returned a " + obj.getClass()
+ " for " + getURI());
+                }
+            } else {
+                // Serialize SAX result
+                TransformerFactory tf = TransformerFactory.newInstance();
+                TransformerHandler th =
+                    ((SAXTransformerFactory) tf).newTransformerHandler();
+                ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+                StreamResult result = new StreamResult(bOut);
+                th.setResult(result);
 
-            toSAX(th);
+                toSAX(th);
 
-            return new ByteArrayInputStream(bOut.toByteArray());
+                return new ByteArrayInputStream(bOut.toByteArray());
+            }
+        } catch (IOException ioe) {
+            throw ioe;
         } catch (Exception e) {
             throw new CascadingIOException("Exception during processing of " + getURI(),
e);
         } finally {
-            if (serializer != null) {
-                serializerSelector.release(serializer);
-            }
-            if (serializerSelector != null) {
-                manager.release(serializerSelector);
-            }
+            cleanup();
         }
     }
 
     /**
-     * Return an {@link OutputStream} to write to.
+     * Return an {@link OutputStream} to write to. This method expects an XML document to
be
+     * written in that stream. To create a binary resource, use {@link #getBinaryOutputStream()}.
      */
     public OutputStream getOutputStream() throws IOException, MalformedURLException {
         if (query != null) {
             throw new MalformedURLException("Cannot modify a resource that includes an XPATH
expression");
         }
-        this.os = new XMLDBOutputStream();
-        return this.os;
+        return new XMLDBOutputStream(false);
+    }
+    
+    /**
+     * Return an {@link OutputStream} to write data to a binary resource.
+     */
+    public OutputStream getBinaryOutputStream() throws IOException, MalformedURLException
{
+        if (query != null) {
+            throw new MalformedURLException("Cannot modify a resource that includes an XPATH
expression");
+        }
+        return new XMLDBOutputStream(true);
+    }
+    
+    /**
+     * Create a new identifier for a resource within a collection. The current source must
be
+     * an existing collection.
+     * 
+     * @throws SourceException 
+     */
+    public String createId() throws SourceException {
+        try {
+            setup();
+            if (status != ST_COLLECTION) {
+                throw new SourceNotFoundException("Collection for createId not found: " +
getURI());
+            }
+            return collection.createId();
+        } catch(XMLDBException xdbe) {
+            throw new SourceException("Cannot get Id for " + getURI(), xdbe);
+        } finally {
+            cleanup();
+        }
     }
 
-    private void writeOutputStream(String content) throws SourceException {
-        String name = null;
-        String base = null;
+    private void writeOutputStream(ByteArrayOutputStream baos, boolean binary) throws SourceException
{
 
         try {
-            if (this.url.endsWith("/")) {
-                name = "";
-                base = this.url.substring(0, this.url.length() - 1);
+            setup();
+            if (status == ST_NO_PARENT) {
+                // If there's no parent collection, create it
+                collection = createCollection(colPath);
+                status = ST_NO_RESOURCE;
+            }
+            
+            // If it's a collection create an id for a child resource.
+            // FIXME(SW): kept for backwards compatibility, but do we really want this?
+            String name;
+            if (status == ST_COLLECTION) {
+                name = collection.createId();
             } else {
-                base = this.url.substring(0, this.url.lastIndexOf("/"));
-                name = this.url.substring(this.url.lastIndexOf("/")+1);
+                name = this.resName;
             }
-            Collection collection = DatabaseManager.getCollection(base, user, password);
 
-            if (name.equals("")) {
-                name = collection.createId();
-                this.url += name;
+            Resource resource;
+            if (binary) {
+                resource = collection.createResource(name, BinaryResource.RESOURCE_TYPE);
+                resource.setContent(baos.toByteArray());
+            } else {
+                resource = collection.createResource(name, XMLResource.RESOURCE_TYPE);
+                // FIXME: potential encoding problems here, as we don't know the one use
in the stream
+                resource.setContent(new String(baos.toByteArray()));
             }
-            Resource resource = collection.createResource(name, "XMLResource");
 
-            resource.setContent(content);
             collection.storeResource(resource);
 
-            getLogger().debug("Written to resource " + name);
+            getLogger().debug("Written to resource " + resName);
         } catch (XMLDBException e) {
-            String message = "Failed to create resource " + name + ": " + e.errorCode;
-            getLogger().debug(message, e);
-            throw new SourceException(message);
+            String message = "Failed to create resource " + resName + ": " + e.errorCode;
+            throw new SourceException(message, e);
+        } finally {
+            cleanup();
         }
     }
 
@@ -511,47 +584,23 @@
      * Delete the source
      */
     public void delete() throws SourceException {
-        String base = null;
-        String name = null;
-        if (this.url.endsWith("/")) {
-            try {
-                // Cut trailing '/'
-                String k = this.url.substring(0, this.url.length() - 1);
-
-                base = k.substring(0, k.lastIndexOf("/"));
-                name = k.substring(k.lastIndexOf("/")+1);
-
-                Collection collection = DatabaseManager.getCollection(base, user, password);
-
+        try {
+            setup();
+            if (status == ST_RESOURCE) {
+                collection.removeResource(resource);
+            } else if (status == ST_COLLECTION) {
+                Collection parent = collection.getParentCollection();
                 CollectionManagementService service =
-                        (CollectionManagementService) collection.getService("CollectionManagementService",
"1.0");
-                service.removeCollection(name);
-            } catch (XMLDBException e) {
-                String message = "Failed to remove collection " + name + ": " + e.errorCode;
-                getLogger().error(message, e);
-                throw new SourceException(message);
-            }
-        } else {
-            try {
-                base = this.url.substring(0, this.url.lastIndexOf("/"));
-                name = this.url.substring(this.url.lastIndexOf("/")+1);
-
-                Collection collection = DatabaseManager.getCollection(base, user, password);
-
-                Resource resource = collection.getResource(name);
-                if (resource == null) {
-                    String message = "Resource " + name + " does not exist";
-                    getLogger().debug(message);
-                    throw new SourceException(message);
-                } else {
-                    collection.removeResource(resource);
-                    getLogger().debug("Removed resource: "+ name);
-                }
-            } catch (XMLDBException e) {
-                String message = "Failed to delete resource " + name + ": " + e.errorCode;
-                getLogger().debug(message, e);
-                throw new SourceException(message);
-            }
+                    (CollectionManagementService) parent.getService("CollectionManagementService",
"1.0");
+                service.removeCollection(collection.getName());
+                close(parent);
+            }
+        } catch (SourceException se) {
+            throw se;
+        } catch (XMLDBException xdbe) {
+            throw new SourceException("Could not delete " + getURI());
+        } finally {
+            cleanup();
         }
     }
 
@@ -562,7 +611,7 @@
      * @return true if the stream can be cancelled
      */
     public boolean canCancel(OutputStream stream) {
-        return !this.os.isClosed();
+        return stream instanceof XMLDBOutputStream && !((XMLDBOutputStream)stream).isClosed();
     }
 
     /**
@@ -572,17 +621,22 @@
      * <p>After cancelling, the stream should no longer be used.</p>
      */
     public void cancel(OutputStream stream) throws IOException {
-        this.os.cancel();
-        this.os = null;
+        if (canCancel(stream)) {
+            ((XMLDBOutputStream)stream).cancel();
+        } else {
+            throw new SourceException("Cannot cancel stream for " + getURI());
+        }
     }
 
-    public class XMLDBOutputStream extends OutputStream {
+    private class XMLDBOutputStream extends OutputStream {
 
         private ByteArrayOutputStream baos;
         private boolean isClosed;
-        public XMLDBOutputStream() {
+        private boolean binary;
+        public XMLDBOutputStream(boolean binary) {
             baos = new ByteArrayOutputStream();
             isClosed = false;
+            this.binary = binary;
         }
 
         public void write(int b) throws IOException {
@@ -599,7 +653,7 @@
 
         public void close() throws IOException, SourceException {
             if (!isClosed) {
-                writeOutputStream(baos.toString());
+                writeOutputStream(baos, this.binary);
                 baos.close();
                 this.isClosed = true;
             }
@@ -618,5 +672,64 @@
         public void cancel() {
             this.isClosed = true;
         }
+    }
+
+    public void makeCollection() throws SourceException {
+        try {
+            createCollection(this.url);
+        } catch (SourceException e) {
+            throw e;
+        } catch (XMLDBException e) {
+            throw new SourceException("Cannot make collection with " + getURI());
+        }
+    }
+
+    public boolean isCollection() {
+        try {
+            setup();
+            return status == ST_COLLECTION;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            cleanup();
+        }
+    }
+
+    public java.util.Collection getChildren() throws SourceException {
+        try {
+            setup();
+            if (status != ST_COLLECTION) {
+                throw new SourceException("Not a collection: " + getURI());
+            }
+            String[] childColl = collection.listChildCollections();
+            String[] childRes = collection.listResources();
+            ArrayList children = new ArrayList(childColl.length + childRes.length);
+            for (int i = 0; i < childColl.length; i++) {
+                children.add(new XMLDBSource(getLogger(), user, password, url + "/" + childColl[i]));
+            }
+            for (int i = 0; i < childRes.length; i++) {
+                children.add(new XMLDBSource(getLogger(), user, password, url + "/" + childRes[i]));
+            }
+            
+            return children;
+        } catch (SourceException e) {
+            throw e;
+        } catch (XMLDBException e) {
+            throw new SourceException("Cannot list children of " + getURI());
+        } finally {
+            cleanup();
+        }
+    }
+    
+    public Source getChild(String name) throws SourceException {
+        return new XMLDBSource(getLogger(), user, password, this.url + "/" + name);
+    }
+
+    public String getName() {
+        return resName;
+    }
+
+    public Source getParent() throws SourceException {
+        return new XMLDBSource(getLogger(), user, password, this.colPath);
     }
 }

Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java?rev=349156&r1=349155&r2=349156&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java
(original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/xmldb/java/org/apache/cocoon/components/source/impl/XMLDBSourceFactory.java
Sat Nov 26 14:49:13 2005
@@ -23,46 +23,46 @@
 import org.apache.avalon.framework.configuration.Configurable;
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.avalon.framework.context.Context;
-import org.apache.avalon.framework.context.ContextException;
-import org.apache.avalon.framework.context.Contextualizable;
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.framework.service.ServiceManager;
-import org.apache.avalon.framework.service.Serviceable;
 import org.apache.avalon.framework.thread.ThreadSafe;
-
 import org.apache.cocoon.components.source.helpers.SourceCredential;
 import org.apache.excalibur.source.Source;
 import org.apache.excalibur.source.SourceFactory;
+import org.xmldb.api.DatabaseManager;
 import org.xmldb.api.base.Database;
 import org.xmldb.api.base.XMLDBException;
-import org.xmldb.api.DatabaseManager;
 
 /**
  * This class implements the xmldb:// pseudo-protocol and allows to get XML
  * content from an XML:DB enabled XML database.
+ * <p>
+ * The configuration of this protocol is as follows:
+ * <pre>
+ *   &lt;source-factory name="xmldb" src="org.apache.cocoon.components.source.impl.XMLDBSourceFactory&gt;
+ *     &lt;driver type="foo" class="org.foomaker.FooXMLDBDriver"
+ *             user="scott" password="tiger"
+ *             collection="//localhost:8080/foo/base-path/"/&gt;
+ *     &lt;driver...
+ *   &lt;source-factory&gt;
+ * </pre>
+ * <p>
+ * The <code>type</code> attribute indicates the database type that will be used
for URLs (e.g.
+ * <code>xmldb:foo:/path/</code>). The <code>collection</code> attribute
specifies a base collection
+ * for paths that do not start with "<code>//</code>".
+ * <p>
+ * The returned sources are traversable, modifiable and xml-izable.
  *
- * @author <a href="mailto:gianugo@rabellino.it">Gianugo Rabellino</a>
- * @version CVS $Id: XMLDBSourceFactory.java,v 1.9 2004/05/17 16:15:18 vgritsenko Exp $
+ * @version CVS $Id$
  */
 public final class XMLDBSourceFactory extends AbstractLogEnabled
-                                      implements SourceFactory, Contextualizable, Configurable,
Serviceable, ThreadSafe {
-
-    /** The ServiceManager instance */
-    protected ServiceManager m_manager;
+                                      implements SourceFactory, Configurable, ThreadSafe
{
 
     /** A Map containing the authentication credentials */
     protected HashMap credentialMap;
-
-    /** The avalon context */
-    protected Context context;
     
-    /* (non-Javadoc)
-     * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
-     */
-    public void contextualize(Context context) throws ContextException {
-        this.context = context;
-    }
+    /** An optional base collection for each of the drivers */
+    protected HashMap baseMap;
+
     /**
      * Configure the instance and initialize XML:DB connections (load and register the drivers).
      */
@@ -70,6 +70,7 @@
     throws ConfigurationException {
 
         credentialMap = new HashMap();
+        baseMap = new HashMap();
 
         Configuration[] drivers = conf.getChildren("driver");
         for (int i = 0; i < drivers.length; i++) {
@@ -80,6 +81,15 @@
             credential.setPrincipal(drivers[i].getAttribute("user", null));
             credential.setPassword(drivers[i].getAttribute("password", null));
             credentialMap.put(type, credential);
+            
+            String base = drivers[i].getAttribute("collection", null);
+            if (base != null && base.length() > 0) {
+                // Ensure the base collection ends with a '/'
+                if (base.charAt(base.length() -  1) != '/') {
+                    base = base + '/';
+                }
+                baseMap.put(type, base);
+            }
 
             if (getLogger().isDebugEnabled()) {
                 getLogger().debug("Initializing XML:DB connection, using driver " + driver);
@@ -111,14 +121,6 @@
     }
 
     /**
-     * Compose this Serviceable object. We need to pass on the
-     * ServiceManager to the actual Source.
-     */
-    public void service(ServiceManager cm) {
-        this.m_manager = cm;
-    }
-
-    /**
      * Resolve the source
      */
     public Source getSource(String location, Map parameters)
@@ -134,17 +136,28 @@
 
         String type = location.substring(start, end);
         SourceCredential credential = (SourceCredential)credentialMap.get(type);
+        
+        if (credential == null) {
+            throw new MalformedURLException("xmldb type '" + type + "' is unknown for URL
" + location);
+        }
+        
+        String base = (String)baseMap.get(type);
 
-        return new XMLDBSource(this.getLogger(),
-                               credential, location,
-                               this.m_manager,
-                               this.context);
+        if (base != null && base.length() > 0) {
+            String path = location.substring(end+1);
+            if (!path.startsWith("//")) {
+                // URL is not absolute, add base, avoiding to double the '/'
+                if (path.charAt(0) == '/') {
+                    path = path.substring(1);
+                }
+                location = location.substring(0, end + 1) + base + path;
+            }
+        }
+
+        return new XMLDBSource(this.getLogger(), credential.getPrincipal(), credential.getPassword(),
location);
     }
 
     public void release(org.apache.excalibur.source.Source source) {
         // nothing to do here
-        if (null != source ) {
-            ((XMLDBSource)source).recycle();
-        }
     }
 }

Modified: cocoon/branches/BRANCH_2_1_X/status.xml
URL: http://svn.apache.org/viewcvs/cocoon/branches/BRANCH_2_1_X/status.xml?rev=349156&r1=349155&r2=349156&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Sat Nov 26 14:49:13 2005
@@ -179,6 +179,11 @@
   <release version="@version@" date="@date@">
 -->
   <release version="2.1.9" date="TBD">
+	 <action dev="SW" type="add">
+      XMLDB: heavy refactoring. The source is now traversable (like file and jcr) and has
two specific
+      methods: <code>createId</code> to create a new document ID for a collection,
and and <code>getBinaryOutputStream</code>
+      to write binary data to the database.
+    </action>
     <action dev="AN" type="add">
       XSP block: Instrument {#expr} interpolation to produce Cocoon stacktrace for parse
errors.
     </action>



Mime
View raw message