/* * eXist Open Source Native XML Database * Copyright (C) 2001-06 Wolfgang M. Meier * meier@ifs.tu-darmstadt.de * http://exist.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.xmldb; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.Properties; import java.util.Random; import javax.xml.transform.OutputKeys; import org.apache.log4j.Logger; import org.exist.EXistException; import org.exist.collections.Collection; import org.exist.collections.IndexInfo; import org.exist.collections.triggers.TriggerException; import org.exist.dom.DocumentImpl; import org.exist.dom.LockToken; import org.exist.security.Permission; import org.exist.security.PermissionDeniedException; import org.exist.security.User; import org.exist.security.xacml.AccessContext; import org.exist.security.xacml.NullAccessContextException; import org.exist.storage.BrokerPool; import org.exist.storage.DBBroker; import org.exist.storage.lock.Lock; import org.exist.storage.serializers.EXistOutputKeys; import org.exist.storage.sync.Sync; import org.exist.storage.txn.TransactionManager; import org.exist.storage.txn.Txn; import org.exist.util.LockException; import org.exist.validation.service.LocalValidationService; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xmldb.api.base.ErrorCodes; import org.xmldb.api.base.Resource; import org.xmldb.api.base.Service; import org.xmldb.api.base.XMLDBException; /** * A local implementation of the Collection interface. This * is used when the database is running in embedded mode. * * Extends Observable to allow status callbacks during indexing. * Methods storeResource notifies registered observers about the * progress of the indexer by passing an object of type ProgressIndicator * to the observer. * *@author wolf */ public class LocalCollection extends Observable implements CollectionImpl { private static Logger LOG = Logger.getLogger(LocalCollection.class); /** * Property to be passed to {@link #setProperty(String, String)}. * When storing documents, pass HTML files through an HTML parser * (NekoHTML) instead of the XML parser. The HTML parser will normalize * the HTML into well-formed XML. */ public final static String NORMALIZE_HTML = "normalize-html"; protected final static Properties defaultProperties = new Properties(); static { defaultProperties.setProperty(OutputKeys.ENCODING, "UTF-8"); defaultProperties.setProperty(OutputKeys.INDENT, "yes"); defaultProperties.setProperty(EXistOutputKeys.EXPAND_XINCLUDES, "yes"); defaultProperties.setProperty(EXistOutputKeys.PROCESS_XSL_PI, "no"); defaultProperties.setProperty(NORMALIZE_HTML, "no"); } protected XmldbURI path = null; protected BrokerPool brokerPool = null; protected Properties properties = new Properties(defaultProperties); protected LocalCollection parent = null; protected User user = null; protected ArrayList observers = new ArrayList(1); protected boolean needsSync = false; private XMLReader userReader = null; private AccessContext accessCtx; private LocalCollection() {} /** * Create a collection with no parent (root collection). * * @param user * @param brokerPool * @param collection * @throws XMLDBException */ public LocalCollection(User user, BrokerPool brokerPool, XmldbURI collection, AccessContext accessCtx) throws XMLDBException { this(user, brokerPool, null, collection, accessCtx); } /** * Create a collection identified by its name. Load the collection from the database. * * @param user * @param brokerPool * @param parent * @param name * @throws XMLDBException */ public LocalCollection( User user, BrokerPool brokerPool, LocalCollection parent, XmldbURI name, AccessContext accessCtx) throws XMLDBException { if(accessCtx == null) throw new NullAccessContextException(); this.accessCtx = accessCtx; if (user == null) user = new User("guest", "guest", "guest"); this.user = user; this.parent = parent; this.brokerPool = brokerPool; this.path = name; if (path == null) path = XmldbURI.ROOT_COLLECTION_URI; path = path.toCollectionPathURI(); getCollection(); } public AccessContext getAccessContext() { return accessCtx; } protected Collection getCollectionWithLock(int lockMode) throws XMLDBException { DBBroker broker = null; Collection collection = null; try { broker = brokerPool.get(user); collection = broker.openCollection(path, lockMode); if(collection == null) throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); collection.setReader(userReader); } catch (EXistException e) { throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e); } finally { brokerPool.release(broker); } return collection; } protected void saveCollection() throws XMLDBException { DBBroker broker = null; Collection collection = null; TransactionManager transact = brokerPool.getTransactionManager(); Txn transaction = transact.beginTransaction(); try { broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.WRITE_LOCK); if(collection == null) throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); broker.saveCollection(transaction, collection); transact.commit(transaction); } catch (IOException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e); } catch (EXistException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e); } catch (PermissionDeniedException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.PERMISSION_DENIED, e.getMessage(), e); } finally { if(collection != null) collection.release(Lock.WRITE_LOCK); brokerPool.release(broker); } } protected Collection getCollection() throws XMLDBException { DBBroker broker = null; try { broker = brokerPool.get(user); org.exist.collections.Collection collection = broker.getCollection(path); if (collection == null) throw new XMLDBException( ErrorCodes.NO_SUCH_COLLECTION, "Collection " + path + " not found"); collection.setReader(userReader); return collection; } catch (EXistException e) { throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e); } finally { brokerPool.release(broker); } } protected boolean checkOwner(Collection collection, User user) throws XMLDBException { return user.getName().equals(collection.getPermissions().getOwner()); } protected boolean checkPermissions(Collection collection, int perm) throws XMLDBException { return collection.getPermissions().validate(user, perm); } /** * Close the current collection. Calling this method will flush all * open buffers to disk. */ public void close() throws XMLDBException { if (needsSync) { DBBroker broker = null; try { broker = brokerPool.get(user); broker.sync(Sync.MAJOR_SYNC); } catch (EXistException e) { throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e); } finally { brokerPool.release(broker); } } } /** * Creates a unique name for a database resource * Uniqueness is only guaranteed within the eXist instance * * The name is based on a hex encoded string of a random integer * and will have the format xxxxxxxx.xml where x is in the range * 0 to 9 and a to f * * @return the unique resource name */ public String createId() throws XMLDBException { //TODO: api change to XmldbURI? Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { XmldbURI id; Random rand = new Random(); boolean ok; do { ok = true; id = XmldbURI.create(Integer.toHexString(rand.nextInt()) + ".xml"); // check if this id does already exist if (collection.hasDocument(id)) ok = false; if (collection.hasSubcollection(id)) ok = false; } while (!ok); return id.toString(); } finally { collection.getLock().release(Lock.READ_LOCK); } } //TODO: api change to XmldbURI? public Resource createResource(String id, String type) throws XMLDBException { if (id == null) id = createId(); XmldbURI idURI; try { idURI = XmldbURI.xmldbUriFor(id); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } Resource r = null; if (type.equals("XMLResource")) r = new LocalXMLResource(user, brokerPool, this, idURI); else if (type.equals("BinaryResource")) r = new LocalBinaryResource(user, brokerPool, this, idURI); else throw new XMLDBException( ErrorCodes.INVALID_RESOURCE, "unknown resource type: " + type); ((AbstractEXistResource)r).isNewResource = true; return r; } //TODO: api change to XmldbURI? public org.xmldb.api.base.Collection getChildCollection(String name) throws XMLDBException { XmldbURI childName = null; XmldbURI childURI; try { childURI = XmldbURI.xmldbUriFor(name); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { if (!checkPermissions(collection, Permission.READ)) throw new XMLDBException( ErrorCodes.PERMISSION_DENIED, "You are not allowed to access this collection"); if(collection.hasChildCollection(childURI)) childName = getPathURI().append(childURI); } finally { collection.release(Lock.READ_LOCK); } if(childName != null) return new LocalCollection(user, brokerPool, this, childName, accessCtx); else return null; } public int getChildCollectionCount() throws XMLDBException { Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { if (checkPermissions(collection, Permission.READ)) return collection.getChildCollectionCount(); else return 0; } finally { collection.getLock().release(Lock.READ_LOCK); } } //TODO: api change to XmldbURI? public String getName() throws XMLDBException { Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { return collection.getURI().toString(); } finally { collection.release(Lock.READ_LOCK); } } public org.xmldb.api.base.Collection getParentCollection() throws XMLDBException { if (getName().equals(DBBroker.ROOT_COLLECTION)) return null; if (parent == null) { // load the collection to check if it is valid DBBroker broker = null; Collection collection = null; try { broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.READ_LOCK); if(collection == null) throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); parent = new LocalCollection(user, brokerPool, null, collection.getParentURI(), accessCtx); } catch (EXistException e) { throw new XMLDBException( ErrorCodes.UNKNOWN_ERROR, "error while retrieving parent collection: " + e.getMessage(), e); } finally { if(collection != null) collection.getLock().release(Lock.READ_LOCK); brokerPool.release(broker); } } return parent; } public String getPath() throws XMLDBException { return path.toString(); } public XmldbURI getPathURI() { return path; } public String getProperty(String property) throws XMLDBException { return properties.getProperty(property); } public Resource getResource(String id) throws XMLDBException { Collection collection = null; DBBroker broker = null; XmldbURI idURI; try { idURI = XmldbURI.xmldbUriFor(id); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } try { broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.READ_LOCK); if(collection == null) throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); if (!checkPermissions(collection, Permission.READ)) throw new XMLDBException( ErrorCodes.PERMISSION_DENIED, "not allowed to read collection"); DocumentImpl document = collection.getDocument(broker, idURI); if (document == null) { LOG.warn("Resource " + idURI + " not found"); return null; } Resource r; if (document.getResourceType() == DocumentImpl.XML_FILE) r = new LocalXMLResource(user, brokerPool, this, idURI); else if (document.getResourceType() == DocumentImpl.BINARY_FILE) r = new LocalBinaryResource(user, brokerPool, this, idURI); else throw new XMLDBException( ErrorCodes.INVALID_RESOURCE, "unknown resource type"); ((AbstractEXistResource)r).setMimeType(document.getMetadata().getMimeType()); return r; } catch (EXistException e) { throw new XMLDBException( ErrorCodes.UNKNOWN_ERROR, "error while retrieving resource: " + e.getMessage(), e); } finally { if(collection != null) collection.release(Lock.READ_LOCK); brokerPool.release(broker); } } public int getResourceCount() throws XMLDBException { Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { if (!checkPermissions(collection, Permission.READ)) return 0; else return collection.getDocumentCount(); } finally { collection.getLock().release(Lock.READ_LOCK); } } /** Possible services: XPathQueryService, XQueryService, * CollectionManagementService (CollectionManager), UserManagementService, * DatabaseInstanceManager, XUpdateQueryService, IndexQueryService, * ValidationService. */ public Service getService(String name, String version) throws XMLDBException { if (name.equals("XPathQueryService")) return new LocalXPathQueryService(user, brokerPool, this, accessCtx); if (name.equals("XQueryService")) return new LocalXPathQueryService(user, brokerPool, this, accessCtx); if (name.equals("CollectionManagementService") || name.equals("CollectionManager")) return new LocalCollectionManagementService(user, brokerPool, this, accessCtx); if (name.equals("UserManagementService")) return new LocalUserManagementService(user, brokerPool, this); if (name.equals("DatabaseInstanceManager")) return new LocalDatabaseInstanceManager(user, brokerPool); if (name.equals("XUpdateQueryService")) return new LocalXUpdateQueryService(user, brokerPool, this); if (name.equals("IndexQueryService")) return new LocalIndexQueryService(user, brokerPool, this); if (name.equals("ValidationService")) return new LocalValidationService(user, brokerPool, this); throw new XMLDBException(ErrorCodes.NO_SUCH_SERVICE); } public Service[] getServices() throws XMLDBException { Service[] services = new Service[7]; services[0] = new LocalXPathQueryService(user, brokerPool, this, accessCtx); services[1] = new LocalCollectionManagementService(user, brokerPool, this, accessCtx); services[2] = new LocalUserManagementService(user, brokerPool, this); services[3] = new LocalDatabaseInstanceManager(user, brokerPool); services[4] = new LocalXUpdateQueryService(user, brokerPool, this); services[5] = new LocalIndexQueryService(user, brokerPool, this); services[6] = new LocalValidationService(user, brokerPool, this); return services; // jmv null; } public boolean isOpen() throws XMLDBException { return true; } //TODO: api change to XmldbURI? public String[] listChildCollections() throws XMLDBException { Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { if (!checkPermissions(collection, Permission.READ)) return new String[0]; String[] collections = new String[collection.getChildCollectionCount()]; int j = 0; for (Iterator i = collection.collectionIterator(); i.hasNext(); j++) collections[j] = ((XmldbURI) i.next()).toString(); return collections; } finally { collection.release(Lock.READ_LOCK); } } public String[] getChildCollections() throws XMLDBException { return listChildCollections(); } public String[] listResources() throws XMLDBException { Collection collection = null; DBBroker broker = null; try { broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.READ_LOCK); if(collection == null) throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); if (!checkPermissions(collection, Permission.READ)) return new String[0]; List allresources = new ArrayList(); DocumentImpl doc; for (Iterator i = collection.iterator(broker); i.hasNext(); ) { doc = (DocumentImpl) i.next(); // Include only when (1) locktoken is present or (2) // locktoken indicates that it is not a null resource LockToken lock = doc.getMetadata().getLockToken(); if(lock==null || (!lock.isNullResource()) ){ allresources.add( doc.getFileURI() ); } } // Copy content of list into String array. int j=0; String[] resources = new String[allresources.size()]; for(Iterator i = allresources.iterator(); i.hasNext(); j++){ resources[j]= ((XmldbURI) i.next()).toString(); } return resources; } catch (EXistException e) { throw new XMLDBException( ErrorCodes.UNKNOWN_ERROR, "error while retrieving resource: " + e.getMessage(), e); } finally { if(collection != null) collection.release(Lock.READ_LOCK); brokerPool.release(broker); } } public String[] getResources() throws XMLDBException { return listResources(); } public void registerService(Service serv) throws XMLDBException { throw new XMLDBException(ErrorCodes.NOT_IMPLEMENTED); } public void removeResource(Resource res) throws XMLDBException { if (res == null) return; XmldbURI resURI; try { resURI = XmldbURI.xmldbUriFor(res.getId()); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } Collection collection = null; DBBroker broker = null; TransactionManager transact = brokerPool.getTransactionManager(); Txn transaction = transact.beginTransaction(); try { if (LOG.isDebugEnabled()) LOG.debug("removing " + resURI); broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.WRITE_LOCK); if(collection == null) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); } //Check that the document exists DocumentImpl doc = collection.getDocument(broker, resURI); if (doc == null) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "resource " + resURI + " not found"); } if (res.getResourceType().equals("XMLResource")) collection.removeXMLResource(transaction, broker, resURI); else collection.removeBinaryResource(transaction, broker, resURI); transact.commit(transaction); } catch (EXistException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e); } catch (PermissionDeniedException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.PERMISSION_DENIED, e.getMessage(), e); } catch (TriggerException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, e.getMessage(), e); } catch (LockException e) { transact.abort(transaction); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e); } finally { if (collection != null) collection.getLock().release(Lock.WRITE_LOCK); brokerPool.release(broker); } needsSync = true; } public void setProperty(String property, String value) throws XMLDBException { properties.setProperty(property, value); } public void storeResource(Resource resource) throws XMLDBException { storeResource(resource, null, null); } public void storeResource(Resource resource, Date a, Date b) throws XMLDBException { if (resource.getResourceType().equals("XMLResource")) { if (LOG.isDebugEnabled()) LOG.debug("storing document " + resource.getId()); ((LocalXMLResource)resource).datecreated =a; ((LocalXMLResource)resource).datemodified =b; storeXMLResource((LocalXMLResource) resource); } else if (resource.getResourceType().equals("BinaryResource")) { if (LOG.isDebugEnabled()) LOG.debug("storing binary resource " + resource.getId()); ((LocalBinaryResource)resource).datecreated =a; ((LocalBinaryResource)resource).datemodified =b; storeBinaryResource((LocalBinaryResource) resource); } else throw new XMLDBException( ErrorCodes.UNKNOWN_RESOURCE_TYPE, "unknown resource type: " + resource.getResourceType()); ((AbstractEXistResource)resource).isNewResource = false; needsSync = true; } private void storeBinaryResource(LocalBinaryResource res) throws XMLDBException { XmldbURI resURI; try { resURI = XmldbURI.xmldbUriFor(res.getId()); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } Collection collection = null; DBBroker broker = null; TransactionManager transact = brokerPool.getTransactionManager(); Txn txn = transact.beginTransaction(); try { broker = brokerPool.get(user); collection = broker.openCollection(path, Lock.WRITE_LOCK); if(collection == null) { transact.abort(txn); throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); } long conLength=res.getStreamLength(); if(conLength!=-1) { collection.addBinaryResource(txn, broker, resURI, res.getStreamContent(), res.getMimeType(), (int)conLength, res.datecreated, res.datemodified); } else { collection.addBinaryResource(txn, broker, resURI, (byte[])res.getContent(), res.getMimeType(), res.datecreated, res.datemodified); } transact.commit(txn); } catch (Exception e) { transact.abort(txn); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "Exception while storing binary resource: " + e.getMessage(), e); } finally { if(collection != null) collection.getLock().release(Lock.WRITE_LOCK); brokerPool.release(broker); } } private void storeXMLResource(LocalXMLResource res) throws XMLDBException { XmldbURI resURI; try { resURI = XmldbURI.xmldbUriFor(res.getId()); } catch(URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI,e); } DBBroker broker = null; TransactionManager transact = brokerPool.getTransactionManager(); Txn txn = transact.beginTransaction(); try { broker = brokerPool.get(user); String uri = null; if(res.file != null) uri = res.file.toURI().toASCIIString(); IndexInfo info = null; Collection collection = null; try { collection = broker.openCollection(path, Lock.WRITE_LOCK); if(collection == null) { transact.abort(txn); throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Collection " + path + " not found"); } Observer observer; for (Iterator i = observers.iterator(); i.hasNext();) { observer = (Observer) i.next(); collection.addObserver(observer); } if (uri != null || res.inputSource!=null) { setupParser(collection, res); info = collection.validateXMLResource(txn, broker, resURI, (uri!=null)?new InputSource(uri):res.inputSource); } else if (res.root != null) info = collection.validateXMLResource(txn, broker, resURI, res.root); else info = collection.validateXMLResource(txn, broker, resURI, res.content); //Notice : the document should now have a Lock.WRITE_LOCK update lock //TODO : check that no exception occurs in order to allow it to be released info.getDocument().getMetadata().setMimeType(res.getMimeType()); if (res.datecreated != null) info.getDocument().getMetadata().setCreated( res.datecreated.getTime()); if (res.datemodified != null) info.getDocument().getMetadata().setLastModified( res.datemodified.getTime()); } finally { if (collection != null) collection.release(Lock.WRITE_LOCK); } if (uri != null || res.inputSource!=null) { collection.store(txn, broker, info, (uri!=null)?new InputSource(uri):res.inputSource, false); } else if (res.root != null) { collection.store(txn, broker, info, res.root, false); } else { collection.store(txn, broker, info, res.content, false); } //Notice : the document should now have its update lock released transact.commit(txn); collection.deleteObservers(); } catch (Exception e) { transact.abort(txn); LOG.error(e); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e); } finally { brokerPool.release(broker); } } private void setupParser(Collection collection, LocalXMLResource res) throws XMLDBException { String normalize = properties.getProperty(NORMALIZE_HTML, "no"); if ((normalize.equalsIgnoreCase("yes") || normalize.equalsIgnoreCase("true")) && (res.getMimeType().equals("text/html") || res.getId().endsWith(".htm") || res.getId().endsWith(".html"))) { try { if (LOG.isDebugEnabled()) LOG.debug("Converting HTML to XML using NekoHTML parser."); Class clazz = Class.forName("org.cyberneko.html.parsers.SAXParser"); XMLReader htmlReader = (XMLReader) clazz.newInstance(); //do not modify the case of elements and attributes htmlReader.setProperty("http://cyberneko.org/html/properties/names/elems", "match"); htmlReader.setProperty("http://cyberneko.org/html/properties/names/attrs", "no-change"); collection.setReader(htmlReader); } catch (Exception e) { LOG.error("Error while involing NekoHTML parser. (" + e.getMessage() + "). If you want to parse non-wellformed HTML files, put " + "nekohtml.jar into directory 'lib/optional'.", e); throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "NekoHTML parser error", e); } } } public Date getCreationTime() throws XMLDBException { Collection collection = getCollectionWithLock(Lock.READ_LOCK); try { return new Date(collection.getCreationTime()); } finally { collection.getLock().release(Lock.READ_LOCK); } } /** * Add a new observer to the list. Observers are just passed * on to the indexer to be notified about the indexing progress. */ public void addObserver(Observer o) { if (!observers.contains(o)) observers.add(o); } /* (non-Javadoc) * @see org.exist.xmldb.CollectionImpl#isRemoteCollection() */ public boolean isRemoteCollection() throws XMLDBException { return false; } /** set user-defined Reader * @param reader */ public void setReader(XMLReader reader){ userReader = reader; } //You probably will have to call this methed from this cast : //((org.exist.xmldb.CollectionImpl)collection).getURI() public XmldbURI getURI() { StringBuilder accessor = new StringBuilder(XmldbURI.XMLDB_URI_PREFIX); //TODO : get the name from client accessor.append("exist"); accessor.append("://"); //No host ;-) accessor.append(""); //No port ;-) if (-1 != -1) accessor.append(":").append(-1); //No context ;-) //accessor.append(getContext()); try { //TODO : cache it when constructed return XmldbURI.create(accessor.toString(), getPath()); } catch (XMLDBException e) { //TODO : should never happen return null; } } }