/*
* Extended and slightly adopted version of the original XMLDBSource found in Apache Cocoon.
* The original license is:
*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id$
*/
package org.exist.cocoon;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
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.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.cocoon.CascadingIOException;
import org.apache.cocoon.xml.IncludeXMLConsumer;
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.source.impl.validity.TimeStampValidity;
import org.apache.excalibur.xml.sax.XMLizable;
import org.exist.external.org.apache.commons.io.output.ByteArrayOutputStream;
import org.exist.util.MimeTable;
import org.exist.util.MimeType;
import org.exist.xmldb.CollectionImpl;
import org.exist.xmldb.EXistResource;
import org.exist.xmldb.ExtendedResource;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Resource;
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;
/**
* This class implements the xmldb:// pseudo-protocol and allows to get XML
* content from an XML:DB enabled XML database.
*
* <p>
* This class starts on
* <a
* href="http://svn.apache.org/viewcvs.cgi/cocoon/blocks/xmldb/trunk/java/org/apache/cocoon/components/source/impl/XMLDBSource.java?rev=359757&view=markup"/
* >XMLDBSource</a> from Cocoon project.
* Some improvments may be useful for Cocoon, some others may be Exist specific.
* Interesting new features :
* </p>
*
* <ul>
* <li>cacheable for a resource read</li>
* <li>handle userinfo urls like <...//user:password@host/...> (for read)</li>
* <li>setContentAsDOM() to avoid encodings problem when writing a resource</li>
* <li>resourceToSAX() plug a lexical handler to get comments of the XML document</li>
* <li>getCollection(), getResource() (xmldb objects)</li>
* <li>getUser(), getPassword()</li>
* </ul>
*
* @version CVS $Id$
*/
public class XMLDBSource extends AbstractLogEnabled
implements ModifiableTraversableSource, XMLizable {
//
// Static Strings used for XML Collection representation
//
/** Source namespace */
public static final String URI = "http://apache.org/cocoon/xmldb/1.0";
/** Source prefix */
public static final String PREFIX = "db";
/** Root element <code><collections></code> */
protected static final String COLLECTIONS = "collections";
/** Root element <code><xmldb:collections></code> (raw name) */
protected static final String QCOLLECTIONS = PREFIX + ":" + COLLECTIONS;
/** Attribute <code>resources</code> on the root element indicates count of resources in the collection */
protected static final String RESOURCE_COUNT_ATTR = "resources";
/** Attribute <code>collections</code> on the root element indicates count of collections in the collection */
protected static final String COLLECTION_COUNT_ATTR = "collections";
protected static final String COLLECTION_BASE_ATTR = "base";
/** Element <code><collection></code> */
protected static final String COLLECTION = "collection";
/** Element <code><xmldb:collection></code> (raw name) */
protected static final String QCOLLECTION = PREFIX + ":" + COLLECTION;
/** Element <code><resource></code> */
protected static final String RESOURCE = "resource";
/** Element <code><resource></code> (raw name) */
protected static final String QRESOURCE = PREFIX + ":" + RESOURCE;
/** Attribute <code>name</code> on the collection/resource element */
protected static final String NAME_ATTR = "name";
/** Root element <code><results></code> */
protected static final String RESULTSET = "results";
/** Root element <code><xmldb:results></code> (raw name) */
protected static final String QRESULTSET = PREFIX + ":" + RESULTSET;
protected static final String QUERY_ATTR = "query";
protected static final String RESULTS_COUNT_ATTR = "resources";
/** Element <code><result></code> */
protected static final String RESULT = "result";
/** Element <code><xmldb:result></code> (raw name) */
protected static final String QRESULT = PREFIX + ":" + RESULT;
protected static final String RESULT_DOCID_ATTR = "docid";
protected static final String RESULT_ID_ATTR = "id";
protected static final String CDATA = "CDATA";
//
// Instance variables
//
/** default encoding to write outputStream */
public String encoding="UTF-8";
/** The requested URL */
public String url;
/** The supplied user */
public String user;
/** The supplied password */
public String password;
/** The part of URL after # sign */
protected String query;
/** The System ID */
protected String systemId;
/** 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;
public Collection collection;
public 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;
private int status = ST_UNKNOWN;
/**
* The constructor.
*
* @param logger the Logger instance.
* @param user
* @param password
* @param srcUrl the URL being queried.
*/
public XMLDBSource(Logger logger,
String user, String password,
String srcUrl) {
enableLogging(logger);
this.user = user;
this.password = password;
// Parse URL (with String methods for efficiency and tolerance)
// Exist improvment, handle user:pass override from URL
// default behavior
this.url = srcUrl;
// allow little queries
int sharp = this.url.indexOf('#');
if (sharp != -1) {
this.url = this.url.substring(0, sharp);
this.query = this.url.substring(sharp + 1);
if (query.length() == 0) query = null;
}
// try userinfo, a '@' before #
// alow things like xmldb:exist://user:password@*/**
int at=this.url.indexOf('@');
int root=this.url.indexOf("//");
if (at > -1 && (sharp == -1 || at < sharp) ) {
// take userinfo first and modify url after
String userinfo=this.url.substring(root + 2, at);
this.url=this.url.substring(0, root+2) + this.url.substring(at + 1);
int column=userinfo.indexOf(":");
if (column != -1) {
this.user=userinfo.substring(0, column);
this.password=userinfo.substring(column + 1);
} else {
this.user=userinfo;
}
}
// Split path in collection and resource
if (this.url.endsWith("/")) {
this.url = this.url.substring(0, this.url.length() - 1);
}
// [giulio]
// [FG] commented till Exist is able to work without URI encoding, to keep compatibility with other possible xmldb
// url = url.replaceAll(" ", "%20");
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;
}
}
}
/** A getter for the xmldb Collection object for this Source */
public Collection getCollection() throws SourceException, XMLDBException {
try {
setup();
return this.collection;
} finally {
cleanup();
}
}
/** A getter for the xmldb Resource object for this Source */
public Resource getResource() throws SourceException, XMLDBException {
try {
setup();
return this.resource;
} finally {
cleanup();
}
}
/** A getter for the user login configured in cocoon.xconf */
public String getUser() {
return this.user;
}
/** A getter for the password configured in cocoon.xconf for the connection */
public String getPassword() {
return this.password;
}
/**
* Stream SAX events to a given ContentHandler. If the requested
* resource is a collection, build an XML view of it.
*/
public void toSAX(ContentHandler handler) throws SAXException {
try {
setup();
if (status == ST_COLLECTION) {
this.collectionToSAX(handler);
} else if (status == ST_RESOURCE) {
this.resourceToSAX(handler);
} else {
throw new SourceNotFoundException(getURI());
}
} catch (SAXException se) {
throw se;
} catch (Exception e) {
throw new SAXException("Error processing " + getURI(), e);
} finally {
cleanup();
}
}
private void resourceToSAX(ContentHandler handler)
throws SAXException, XMLDBException, SourceException {
if (!(resource instanceof XMLResource)) {
throw new SAXException("Not an XML resource: " + getURI());
}
if (query != null) {
// Query resource
if (getLogger().isDebugEnabled()) {
getLogger().debug("Querying resource " + resName + " from collection " + url + "; query= " + this.query);
}
queryToSAX(handler, collection, resName);
} else {
// Return entire resource
if (getLogger().isDebugEnabled()) {
getLogger().debug("Obtaining resource " + resName + " from collection " + colPath);
}
// <frederic.glorieux@ajlsm.com> exist specific improvements
if (resource instanceof org.exist.xmldb.EXistResource ) {
// To output comments
((org.exist.xmldb.EXistResource)resource).setLexicalHandler((LexicalHandler)handler);
}
((XMLResource)resource).getContentAsSAX(handler);
}
}
private void collectionToSAX(ContentHandler handler)
throws SAXException, XMLDBException {
AttributesImpl attributes = new AttributesImpl();
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);
}
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);
}
// 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, XMLDBException {
AttributesImpl attributes = new AttributesImpl();
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()));
handler.startDocument();
handler.startPrefixMapping(PREFIX, URI);
handler.startElement(URI, RESULTSET, QRESULTSET, attributes);
IncludeXMLConsumer includeHandler = new IncludeXMLConsumer(handler);
// Print search results
ResourceIterator results = resultSet.getIterator();
while (results.hasMoreResources()) {
XMLResource result = (XMLResource)results.nextResource();
final String id = result.getId();
final String documentId = result.getDocumentId();
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.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);
}
handler.endElement(URI, RESULTSET, QRESULTSET);
handler.endPrefixMapping(PREFIX);
handler.endDocument();
}
public String getURI() {
return url;
}
public long getContentLength() {
long result=-1;
try {
setup();
result = new Integer(((EXistResource)this.resource).getContentLength()).longValue();
} catch (Exception e){
if (getLogger().isDebugEnabled()) {
getLogger().debug("getContentLength() for " + resName + " from collection " + url + " failed: " + e.getMessage());
}
}
finally {
cleanup();
}
return result;
}
public long getLastModified() {
long result=0;
try {
setup();
if (this.status == ST_COLLECTION){
result = ((CollectionImpl)this.collection).getCreationTime().getTime();
}else if(this.status == ST_RESOURCE){
result = ((EXistResource)this.resource).getLastModificationTime().getTime();
}
} catch (Exception e){
if (getLogger().isDebugEnabled()) {
getLogger().debug("getLastModified() for " + resName + " from collection " + url + " failed: " + e.getMessage());
}
}
finally {
cleanup();
}
return result;
}
public boolean exists() {
try {
setup();
return status == ST_COLLECTION || status == ST_RESOURCE;
} catch (Exception e) {
return false;
} finally {
cleanup();
}
}
public String getMimeType() {
return null;
}
public String getScheme() {
return SourceUtil.getScheme(url);
}
public SourceValidity getValidity() {
try {
if (resource == null || collection==null) setup();
if (resource != null && resource instanceof org.exist.xmldb.AbstractEXistResource)
return new TimeStampValidity(((org.exist.xmldb.AbstractEXistResource)resource).getLastModificationTime().getTime());
/*
if (collection != null && resource instanceof org.exist.xmldb.AbstractEXistResource)
return new TimeStampValidity(((org.exist.xmldb.AbstractEXistResource)resource).getLastModificationTime().getTime());
*/
}
catch (XMLDBException e) {}
catch (SourceException e) {}
return null;
}
public void refresh() {
}
/**
* Get an InputSource for the given URL.
*/
public InputStream getInputStream()
throws IOException {
try {
setup();
// Check if it's binary
if (resource instanceof ExtendedResource) {
return ((ExtendedResource)resource).getStreamContent();
} 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);
return new ByteArrayInputStream(bOut.toByteArray());
}
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
throw new CascadingIOException("Exception during processing of " + getURI(), e);
} finally {
cleanup();
}
}
/**
* 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");
}
return new XMLDBOutputStream(false);
}
/**
* set content as DOM
*
* @see <a href="http://exist.sourceforge.net/api/org/xmldb/api/modules/XMLResource.html#setContentAsDOM(org.w3c.dom.Node)">XMLDB API</a>
*/
public void setContentAsDOM(Node doc) throws IOException, MalformedURLException {
// author frederic.glorieux@ajlsm.com
try {
if (query != null) {
throw new MalformedURLException("Cannot modify a resource that includes an XPATH expression");
}
setup();
if (status == ST_NO_PARENT) {
// If there's no parent collection, create it
collection = createCollection(colPath);
status = ST_NO_RESOURCE;
}
resource = collection.createResource(this.resName, XMLResource.RESOURCE_TYPE);
((XMLResource)resource).setContentAsDOM(doc);
collection.storeResource(resource);
} catch (XMLDBException e) {
String message = "Failed to create resource " + resName + ": " + e.errorCode;
e.printStackTrace(System.out);
throw new SourceException(message, e);
}
}
/**
* get content as DOM
* @see <a href="http://exist.sourceforge.net/api/org/xmldb/api/modules/XMLResource.html#setContentAsDOM(org.w3c.dom.Node)">XMLDB API</a>
*/
public Node getContentAsDOM() throws IOException, MalformedURLException {
try {
setup();
if (!(resource instanceof XMLResource)) {
throw new SourceException("Not an XML resource: " + getURI());
}
if (query != null) {
throw new MalformedURLException("Not yet available for queries, only for single resource.");
}
String name=this.resName;
resource = collection.createResource(name, XMLResource.RESOURCE_TYPE);
return ((XMLResource)resource).getContentAsDOM();
} catch (XMLDBException e) {
String message = "Failed to create resource " + resName + ": " + e.errorCode;
throw new SourceException(message, e);
}
}
/**
* 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(ByteArrayOutputStream baos, boolean binary) throws SourceException {
try {
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 {
name = this.resName;
}
String mimeType;
Resource resource;
if (binary) {
resource = collection.createResource(name, BinaryResource.RESOURCE_TYPE);
resource.setContent(baos.toByteArray());
mimeType = MimeType.BINARY_TYPE.getName();
} else {
resource = collection.createResource(name, XMLResource.RESOURCE_TYPE);
// FIXME: potential encoding problems here, as we don't know the one use in the stream
// frederic.glorieux@ajlsm.com : Yes, it is, here a quick hack, default encoding for XML=UTF-8
resource.setContent(new String(baos.toByteArray(), encoding) );
mimeType = MimeType.XML_TYPE.getName();
}
MimeType mime = MimeTable.getInstance().getContentTypeFor(name);
if (mime != null) {
mimeType = mime.getName();
}
((EXistResource)resource).setMimeType(mimeType);
collection.storeResource(resource);
getLogger().debug("Written to resource " + resName);
} catch (XMLDBException e) {
String message = "Failed to create resource " + resName + ": " + e.errorCode;
throw new SourceException(message, e);
} catch (UnsupportedEncodingException e) {
String message = "Encoding for the resource " + resName + encoding;
throw new SourceException(message, e);
} finally {
cleanup();
}
}
/** Set a default encoding for outputStream */
public void setEncoding(String s) {
this.encoding=s;
}
/** Set a default encoding for outputStream */
public String getEncoding() {
return this.encoding;
}
/**
* Delete the source
*/
public void delete() throws SourceException {
try {
setup();
if (status == ST_RESOURCE) {
collection.removeResource(resource);
} else if (status == ST_COLLECTION) {
Collection parent = collection.getParentCollection();
CollectionManagementService service =
(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();
}
}
/**
* Can the data sent to an <code>OutputStream</code> returned by
* {@link #getOutputStream()} be cancelled ?
*
* @return true if the stream can be cancelled
*/
public boolean canCancel(OutputStream stream) {
return stream instanceof XMLDBOutputStream && !((XMLDBOutputStream)stream).isClosed();
}
/**
* Cancel the data sent to an <code>OutputStream</code> returned by
* {@link #getOutputStream()}.
*
* <p>After cancelling, the stream should no longer be used.</p>
*/
public void cancel(OutputStream stream) throws IOException {
if (canCancel(stream)) {
((XMLDBOutputStream)stream).cancel();
} else {
throw new SourceException("Cannot cancel stream for " + getURI());
}
}
private class XMLDBOutputStream extends OutputStream {
private ByteArrayOutputStream baos;
private boolean isClosed;
private boolean binary;
public XMLDBOutputStream(boolean binary) {
baos = new ByteArrayOutputStream();
isClosed = false;
this.binary = binary;
}
public void write(int b) throws IOException {
baos.write(b);
}
public void write(byte b[]) throws IOException {
baos.write(b);
}
public void write(byte b[], int off, int len) throws IOException {
baos.write(b, off, len);
}
public void close() throws IOException, SourceException {
if (!isClosed) {
writeOutputStream(baos, this.binary);
baos.close();
this.isClosed = true;
}
}
public void flush() throws IOException {
}
public int size() {
return baos.size();
}
public boolean isClosed() {
return this.isClosed;
}
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);
}
}