Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 794   Methods: 30
NCLOC: 438   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CollectionImpl.java 76.8% 82% 90% 81.8%
coverage coverage
 1    /*
 2    * Licensed to the Apache Software Foundation (ASF) under one or more
 3    * contributor license agreements. See the NOTICE file distributed with
 4    * this work for additional information regarding copyright ownership.
 5    * The ASF licenses this file to You under the Apache License, Version 2.0
 6    * (the "License"); you may not use this file except in compliance with
 7    * the License. You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    *
 17    * $Id: CollectionImpl.java 712565 2008-11-09 21:52:26Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.client.xmldb.xmlrpc;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.client.xmldb.ResourceSetImpl;
 25    import org.apache.xindice.client.xmldb.XindiceCollection;
 26    import org.apache.xindice.client.xmldb.resources.BinaryResourceImpl;
 27    import org.apache.xindice.client.xmldb.resources.XMLResourceImpl;
 28    import org.apache.xindice.core.FaultCodes;
 29    import org.apache.xindice.core.meta.MetaData;
 30    import org.apache.xindice.server.rpc.RPCDefaultMessage;
 31    import org.apache.xindice.server.rpc.RPCMessageInterface;
 32    import org.apache.xindice.util.SymbolDeserializer;
 33    import org.apache.xindice.xml.TextWriter;
 34    import org.apache.xindice.xml.SymbolTable;
 35    import org.apache.xindice.xml.dom.DOMParser;
 36    import org.apache.xindice.xml.dom.DocumentImpl;
 37    import org.apache.xmlrpc.XmlRpcException;
 38    import org.apache.xmlrpc.client.XmlRpcClient;
 39    import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
 40   
 41    import org.w3c.dom.Document;
 42    import org.xml.sax.InputSource;
 43    import org.xmldb.api.base.Collection;
 44    import org.xmldb.api.base.ErrorCodes;
 45    import org.xmldb.api.base.Resource;
 46    import org.xmldb.api.base.ResourceSet;
 47    import org.xmldb.api.base.XMLDBException;
 48    import org.xmldb.api.modules.XMLResource;
 49   
 50    import javax.xml.parsers.DocumentBuilderFactory;
 51   
 52    import java.io.StringReader;
 53    import java.net.URL;
 54    import java.util.HashMap;
 55    import java.util.Hashtable;
 56    import java.util.Map;
 57    import java.util.StringTokenizer;
 58   
 59    /**
 60    * Implementation of XML:DB's <code>Collection</code> interface using
 61    * XML-RPC to interact with database server
 62    *
 63    * @author <a href="mailto:james.bates@amplexor.com">James Bates</a>
 64    * @author <a href="mailto:kstaken@xmldatabases.org">Kimbro Staken</a>
 65    * @version $Revision: 712565 $, $Date: 2008-11-09 21:52:26 +0000 (Sun, 09 Nov 2008) $
 66    */
 67    public class CollectionImpl extends XindiceCollection {
 68   
 69    private static final Log log = LogFactory.getLog(CollectionImpl.class);
 70   
 71    /**
 72    * The XML-RPC client stub, connected to the server
 73    */
 74    private XmlRpcClient client;
 75   
 76    /**
 77    * Cached symbol table
 78    */
 79    private SymbolDeserializer symsDeserializer;
 80   
 81    /**
 82    * Creates new <code>CollectionImpl</code> instance representing connection
 83    * to server collection.
 84    *
 85    * @param client XML-RPC client connected to the Xindice XML-RPC server.
 86    * @param collPath is the name of the collection to open.
 87    * @exception XMLDBException thrown if a connection could not be established,
 88    * because of URL syntax errors, or connection failure, or if no
 89    * collection with path <code>collPath</code> could be located.
 90    */
 91  1140 public CollectionImpl(XmlRpcClient client, String collPath) throws XMLDBException {
 92  1140 super(collPath);
 93  1140 this.client = client;
 94   
 95    /* Just check the collection does actually exist */
 96  1140 Map params = new HashMap(3);
 97  1140 params.put(RPCDefaultMessage.COLLECTION, collPath);
 98  1140 String exists = (String) runRemoteCommand("GetCollectionConfiguration", params);
 99   
 100  1139 if (!"yes".equals(exists)) {
 101  1 throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION,
 102    FaultCodes.COL_COLLECTION_NOT_FOUND,
 103    "Collection not found: " + collPath);
 104    }
 105   
 106  1138 symsDeserializer = new SymbolDeserializer();
 107    }
 108   
 109    /**
 110    * Returns URL used by the XML-RPC client.
 111    *
 112    * @return URL used by the XML-RPC client.
 113    */
 114  1 private URL getURL() {
 115  1 return ((XmlRpcClientConfigImpl) client.getClientConfig()).getServerURL();
 116    }
 117   
 118    /**
 119    * Submits a command for RPC to database server
 120    *
 121    * @param cmdName command name
 122    * @param params hashtable containing named parameters to send to server
 123    * @return the return value from the server. Type of return value depends on
 124    * command.
 125    *
 126    * @exception XMLDBException thrown if XML-RPC reports an exception.
 127    */
 128  2382 private Object runRemoteCommand(String cmdName, Map params) throws XMLDBException {
 129  2382 try {
 130  2382 params.put(RPCMessageInterface.MESSAGE_PARAM, cmdName);
 131  2382 return ((Map) client.execute("run", new Object[]{ params })).get(RPCDefaultMessage.RESULT);
 132    } catch (XmlRpcException e) {
 133  15 if (log.isDebugEnabled()) {
 134  0 log.debug("Got XmlRpc exception running command " + cmdName + ", code: " + e.code + ", msg: " + e.getMessage());
 135    }
 136   
 137  15 if (e.code != 0) {
 138  14 throw new XMLDBException(e.code / FaultCodes.MAX_CODE,
 139    e.code % FaultCodes.MAX_CODE,
 140    e.getMessage());
 141    }
 142   
 143  1 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, FaultCodes.GEN_GENERAL_ERROR,
 144    "Failed to execute command '" + cmdName + "' on server: " + getURL() + ", message: " + e.getMessage(), e);
 145    }
 146    }
 147   
 148    /**
 149    * Retrieves a <code>Resource</code> from the database. If the
 150    * <code>Resource</code> could not be
 151    * located a null value will be returned.
 152    *
 153    * @param id the unique id for the requested resource.
 154    * @return The retrieved <code>Resource</code> instance.
 155    * @exception XMLDBException with expected error codes.<br />
 156    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 157    * specific errors that occur.<br />
 158    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 159    * method has been called on the <code>Collection</code><br />
 160    */
 161  178 public Resource getResource(String id) throws XMLDBException {
 162  178 checkOpen();
 163   
 164  178 try {
 165  178 if (id == null) {
 166  2 return null;
 167    }
 168   
 169  176 Map params = new HashMap();
 170  176 params.put(RPCDefaultMessage.COLLECTION, collPath);
 171  176 params.put(RPCDefaultMessage.NAME, id);
 172  176 params.put(RPCDefaultMessage.COMPRESSED, "true");
 173  176 params.put(RPCDefaultMessage.TIMESTAMP, Long.toString(symsDeserializer.getLastModified()));
 174   
 175  176 Object result = runRemoteCommand("GetResource", params);
 176   
 177  176 if (result == null) {
 178    // No resource found
 179  5 return null;
 180   
 181  171 } else if (result instanceof Map) {
 182    // Result is compressed XML.
 183  169 Map compressed = (Map) result;
 184  169 SymbolTable syms = symsDeserializer.getSymbols(compressed);
 185  169 return new XMLResourceImpl(id, id, this, syms, (byte[]) compressed.get("document"));
 186   
 187  2 } else if (result instanceof byte[]) {
 188    // Result is binary.
 189  2 return new BinaryResourceImpl(id, this, (byte[]) result);
 190   
 191    } else {
 192    // Result is XML string.
 193  0 return new XMLResourceImpl(id, this, (String) result);
 194    }
 195   
 196    } catch (XMLDBException x) {
 197   
 198  0 throw x; // propagate any xmldb exception.
 199    } catch (Exception e) {
 200   
 201  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 202    }
 203    }
 204   
 205    /**
 206    * Returns the number of resources currently stored in this collection or 0
 207    * if the collection is empty.
 208    *
 209    * @return the number of resource in the collection.
 210    * @exception XMLDBException with expected error codes.<br />
 211    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 212    * specific errors that occur.<br />
 213    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 214    * method has been called on the <code>Collection</code><br />
 215    */
 216  21 public int getResourceCount() throws XMLDBException {
 217   
 218  21 checkOpen();
 219  21 try {
 220   
 221  21 Map params = new HashMap();
 222  21 params.put(RPCDefaultMessage.COLLECTION, collPath);
 223  21 return ((Integer) runRemoteCommand("GetDocumentCount", params)).intValue();
 224    } catch (XMLDBException x) {
 225   
 226  0 throw x; // propagate any xmldb exception.
 227    } catch (Exception e) {
 228   
 229  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 230    }
 231    }
 232   
 233    /**
 234    * Stores the provided resource into the database. If the resource does not
 235    * already exist it will be created. If it does already exist it will be
 236    * updated.
 237    *
 238    * @param res the resource to store in the database.
 239    * @exception XMLDBException with expected error codes.<br />
 240    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 241    * specific errors that occur.<br />
 242    * <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
 243    * not valid.
 244    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 245    * method has been called on the <code>Collection</code><br />
 246    */
 247  411 public void storeResource(Resource res) throws XMLDBException {
 248   
 249  411 if (res.getContent() == null) {
 250  0 throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "no resource data");
 251    }
 252   
 253  411 checkOpen();
 254  411 try {
 255  411 Map params = createParams(res);
 256  411 String name = (String) runRemoteCommand("SetResource", params);
 257  411 seResourcetId(res, name);
 258    } catch (XMLDBException x) {
 259  0 throw x; // propagate any xmldb exception.
 260    } catch (Exception e) {
 261  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 262    }
 263    }
 264   
 265    /**
 266    * Inserts the provided resource into the database.
 267    *
 268    * @param res the resource to insert into the database.
 269    * @exception XMLDBException with expected error codes.<br />
 270    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 271    * specific errors that occur.<br />
 272    * <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
 273    * not valid.
 274    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 275    * method has been called on the <code>Collection</code><br />
 276    */
 277  2 public void insertResource(Resource res) throws XMLDBException {
 278   
 279  2 if (res.getContent() == null) {
 280  0 throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "no resource data");
 281    }
 282   
 283  2 checkOpen();
 284  2 try {
 285  2 Map params = createParams(res);
 286  2 String name = (String) runRemoteCommand("InsertResource", params);
 287  1 seResourcetId(res, name);
 288    } catch (XMLDBException x) {
 289  1 throw x; // propagate any xmldb exception.
 290    } catch (Exception e) {
 291  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 292    }
 293    }
 294   
 295    /**
 296    * Updates the provided resource in the database.
 297    *
 298    * @param res the resource to update in the database.
 299    * @exception XMLDBException with expected error codes.<br />
 300    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 301    * specific errors that occur.<br />
 302    * <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
 303    * not valid.
 304    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 305    * method has been called on the <code>Collection</code><br />
 306    */
 307  2 public void updateResource(Resource res) throws XMLDBException {
 308   
 309  2 if (res.getContent() == null) {
 310  0 throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "no resource data");
 311    }
 312   
 313  2 checkOpen();
 314  2 try {
 315  2 Map params = createParams(res);
 316  2 String name = (String) runRemoteCommand("UpdateResource", params);
 317  1 seResourcetId(res, name);
 318    } catch (XMLDBException x) {
 319  1 throw x; // propagate any xmldb exception.
 320    } catch (Exception e) {
 321  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 322    }
 323    }
 324   
 325  415 private Map createParams(Resource res) throws XMLDBException {
 326  415 Map params = new HashMap();
 327  415 params.put(RPCDefaultMessage.COLLECTION, collPath);
 328  415 params.put(RPCDefaultMessage.NAME, res.getId());
 329  415 params.put(RPCDefaultMessage.DOCUMENT, res.getContent());
 330  415 return params;
 331    }
 332   
 333  413 private void seResourcetId(Resource res, String name) {
 334  413 if (res instanceof XMLResource) {
 335  409 ((XMLResourceImpl) res).setId(name);
 336    } else {
 337  4 ((BinaryResourceImpl) res).setId(name);
 338    }
 339    }
 340   
 341    /* see superclass for documentation */
 342  1638 public boolean isOpen() {
 343   
 344  1638 return (client != null);
 345    }
 346   
 347    /* see superclass for documentation */
 348  0 public String getURI() {
 349   
 350  0 return "xmldb:" + DatabaseImpl.DRIVER_NAME + "://" +
 351    getURL().getHost() + ':' + getURL().getPort() +
 352    collPath;
 353    }
 354   
 355    /**
 356    * Returns a <code>Collection</code> instance for the requested child collection
 357    * if it exists.
 358    *
 359    * @param name the name of the child collection to retrieve.
 360    * @return the requested child collection or null if it couldn't be found.
 361    * @exception XMLDBException with expected error codes.<br />
 362    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 363    * specific errors that occur.<br />
 364    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 365    * method has been called on the <code>Collection</code><br />
 366    */
 367  128 public Collection getChildCollection(String name) throws XMLDBException {
 368   
 369  128 if (name.indexOf('/') != -1) {
 370  1 throw new XMLDBException(ErrorCodes.INVALID_COLLECTION);
 371    }
 372   
 373  127 try {
 374  127 return new CollectionImpl(client, collPath + "/" + name);
 375    } catch (XMLDBException e) {
 376   
 377  0 if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
 378    // per getChildCollection contract, return null if not found
 379  0 return null;
 380    }
 381   
 382  0 throw e;
 383    }
 384    }
 385   
 386    /**
 387    * Creates a new unique ID within the context of the <code>Collection</code>
 388    *
 389    * @return the created id as a string.
 390    * @exception XMLDBException with expected error codes.<br />
 391    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 392    * specific errors that occur.<br />
 393    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 394    * method has been called on the <code>Collection</code><br />
 395    */
 396  4 public String createId() throws XMLDBException {
 397   
 398  4 checkOpen();
 399  4 try {
 400   
 401  4 Map params = new HashMap();
 402  4 params.put(RPCDefaultMessage.COLLECTION, collPath);
 403  4 return (String) runRemoteCommand("CreateNewOID", params);
 404    } catch (XMLDBException x) {
 405   
 406  0 throw x; // propagate any xmldb exception.
 407    } catch (Exception e) {
 408   
 409  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 410    }
 411    }
 412   
 413    /**
 414    * Releases all resources consumed by the <code>Collection</code>.
 415    * The <code>close</code> method must
 416    * always be called when use of a <code>Collection</code> is complete. It is
 417    * not safe to use a <code>Collection</code> after the <code>close</code>
 418    * method has been called.
 419    *
 420    * @exception XMLDBException with expected error codes.<br />
 421    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 422    * specific errors that occur.<br />
 423    */
 424  1 public void close() throws XMLDBException {
 425  1 client = null;
 426    }
 427   
 428    /**
 429    * Returns the parent collection for this collection or null if no parent
 430    * collection exists.
 431    *
 432    * @return the parent <code>Collection</code> instance.
 433    * @exception XMLDBException with expected error codes.<br />
 434    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 435    * specific errors that occur.<br />
 436    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 437    * method has been called on the <code>Collection</code><br />
 438    */
 439  4 public Collection getParentCollection() throws XMLDBException {
 440   
 441    // If there's only one slash then it's the root.
 442  4 if (collPath.lastIndexOf("/") == 0) {
 443  1 return null;
 444    }
 445   
 446  3 try {
 447  3 return new CollectionImpl(client, collPath.substring(0, collPath.lastIndexOf('/')));
 448    } catch (XMLDBException e) {
 449  0 if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
 450    // per getParentCollection contract, return null if no parent
 451  0 return null;
 452    }
 453  0 throw e;
 454    }
 455    }
 456   
 457    /**
 458    * Removes the <code>Resource</code> from the database.
 459    *
 460    * @param res the resource to remove.
 461    * @exception XMLDBException with expected error codes.<br />
 462    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 463    * specific errors that occur.<br />
 464    * <code>ErrorCodes.INVALID_RESOURCE</code> if the <code>Resource</code> is
 465    * not valid.<br />
 466    * <code>ErrorCodes.NO_SUCH_RESOURCE</code> if the <code>Resource</code> is
 467    * not known to this <code>Collection</code>.
 468    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 469    * method has been called on the <code>Collection</code><br />
 470    */
 471  155 public void removeResource(Resource res) throws XMLDBException {
 472   
 473  155 if (res == null || res.getId() == null || res.getId().length() == 0) {
 474    // Query result resource will have null ID
 475  3 throw new XMLDBException(ErrorCodes.INVALID_RESOURCE,
 476    "Resource passed is null or its ID is empty.");
 477    }
 478   
 479  152 checkOpen();
 480  152 try {
 481    // TODO: Test BinaryResource workings
 482  152 Map params = new HashMap();
 483  152 params.put(RPCDefaultMessage.COLLECTION, collPath);
 484  152 params.put(RPCDefaultMessage.NAME, res.getId());
 485  152 runRemoteCommand("RemoveDocument", params);
 486    } catch (XMLDBException x) {
 487   
 488  0 throw x; // propagate any xmldb exception.
 489    } catch (Exception e) {
 490  0 throw new XMLDBException(ErrorCodes.NO_SUCH_RESOURCE, e);
 491    }
 492    }
 493   
 494    /**
 495    * Returns a list of collection names naming all child collections
 496    * of the current collection. If no child collections exist an empty list is
 497    * returned.
 498    *
 499    * @return an array containing collection names for all child
 500    * collections.
 501    * @exception XMLDBException with expected error codes.<br />
 502    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 503    * specific errors that occur.<br />
 504    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 505    * method has been called on the <code>Collection</code><br />
 506    */
 507  5 public String[] listChildCollections() throws XMLDBException {
 508   
 509  5 checkOpen();
 510  5 try {
 511   
 512  5 Map params = new HashMap();
 513  5 params.put(RPCDefaultMessage.COLLECTION, collPath);
 514    // XML-RPC returns array of String objects
 515  5 Object[] result = (Object[]) runRemoteCommand("ListCollections", params);
 516  5 String[] list = new String[result.length];
 517    //noinspection SuspiciousSystemArraycopy
 518  5 System.arraycopy(result, 0, list, 0, result.length);
 519   
 520  5 return list;
 521    } catch (XMLDBException x) {
 522   
 523  0 throw x; // propagate any xmldb exception.
 524    } catch (Exception e) {
 525   
 526  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 527    }
 528    }
 529   
 530    /**
 531    * Returns the number of child collections under this
 532    * <code>Collection</code> or 0 if no child collections exist.
 533    *
 534    * @return the number of child collections.
 535    * @exception XMLDBException with expected error codes.<br />
 536    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 537    * specific errors that occur.<br />
 538    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 539    * method has been called on the <code>Collection</code><br />
 540    */
 541  6 public int getChildCollectionCount() throws XMLDBException {
 542   
 543  6 checkOpen();
 544  6 try {
 545   
 546  6 Map params = new HashMap();
 547  6 params.put(RPCDefaultMessage.COLLECTION, collPath);
 548  6 Integer result = (Integer) runRemoteCommand("GetCollectionCount", params);
 549  6 return result.intValue();
 550   
 551    } catch (XMLDBException x) {
 552   
 553  0 throw x; // propagate any xmldb exception.
 554    } catch (Exception e) {
 555  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 556    }
 557    }
 558   
 559    /**
 560    * Returns a list of the ids for all resources stored in the collection.
 561    *
 562    * @return a string array containing the names for all
 563    * <code>Resource</code>s in the collection.
 564    * @exception XMLDBException with expected error codes.<br />
 565    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 566    * specific errors that occur.<br />
 567    * <code>ErrorCodes.COLLECTION_CLOSED</code> if the <code>close</code>
 568    * method has been called on the <code>Collection</code><br />
 569    */
 570  7 public String[] listResources() throws XMLDBException {
 571   
 572  7 checkOpen();
 573  7 try {
 574   
 575  7 Map params = new HashMap();
 576  7 params.put(RPCDefaultMessage.COLLECTION, collPath);
 577    // XML-RPC returns array of String objects
 578  7 Object[] result = (Object[]) runRemoteCommand("ListDocuments", params);
 579  7 String[] list = new String[result.length];
 580    //noinspection SuspiciousSystemArraycopy
 581  7 System.arraycopy(result, 0, list, 0, result.length);
 582   
 583  7 return list;
 584    } catch (XMLDBException x) {
 585   
 586  0 throw x; // propagate any xmldb exception.
 587    } catch (Exception e) {
 588   
 589  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
 590    }
 591    }
 592   
 593    /* see superclass for documentation */
 594  119 public ResourceSet query(String name, String queryLang, String query, Hashtable nsMap) throws XMLDBException {
 595   
 596  119 checkOpen();
 597  119 try {
 598  119 Map params = new HashMap();
 599  119 params.put(RPCDefaultMessage.COLLECTION, collPath);
 600  119 params.put(RPCDefaultMessage.TYPE, queryLang);
 601  119 params.put(RPCDefaultMessage.NAMESPACES, nsMap);
 602  119 params.put(RPCDefaultMessage.QUERY, query);
 603   
 604  119 if (name != null) {
 605  2 params.put(RPCDefaultMessage.NAME, name);
 606    }
 607   
 608  119 String result = (String) runRemoteCommand("Query", params);
 609  119 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 610  119 dbf.setNamespaceAware(true);
 611  119 Document resultDoc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(result)));
 612   
 613  119 return new ResourceSetImpl(this, resultDoc);
 614    } catch (Exception e) {
 615  0 throw FaultCodes.createXMLDBException(FaultCodes.QRY_PROCESSING_ERROR, "Query error", e);
 616    }
 617    }
 618   
 619    /* see superclass for documentation */
 620  0 public Collection createCollection(String name) throws XMLDBException {
 621  0 return createCollection(name, null);
 622    }
 623   
 624    // See superclass for documentation. Note that first argument is the path, not the name.
 625  130 public Collection createCollection(String path, Document configuration) throws XMLDBException {
 626  130 checkOpen();
 627  130 try {
 628  130 Map params = new HashMap(5);
 629  130 params.put(RPCDefaultMessage.COLLECTION, collPath);
 630  130 params.put(RPCDefaultMessage.NAME, path);
 631  130 if (configuration != null) {
 632  130 params.put(RPCDefaultMessage.CONFIGURATION, TextWriter.toString(configuration));
 633    }
 634   
 635  130 runRemoteCommand("CreateCollection", params);
 636   
 637    // Traverse path to get newly created collection
 638  126 org.xmldb.api.base.Collection col = this;
 639  126 if (path.indexOf("/") != -1) {
 640  1 StringTokenizer st = new StringTokenizer(path, "/");
 641  2 while (col != null && st.hasMoreTokens()) {
 642  2 path = st.nextToken().trim();
 643  2 if (path.length() == 0) {
 644  0 continue;
 645    }
 646   
 647  2 if (st.hasMoreTokens()) {
 648  1 col = col.getChildCollection(path);
 649    } else {
 650  1 break;
 651    }
 652    }
 653    }
 654   
 655  126 return col.getChildCollection(path);
 656    } catch (XMLDBException x) {
 657   
 658  4 throw x; // propagate any xmldb exception.
 659    } catch (Exception e) {
 660  0 throw new XMLDBException(ErrorCodes.INVALID_COLLECTION, "Cannot create child collection", e);
 661    }
 662    }
 663   
 664    /* see superclass for documentation */
 665  129 public void removeCollection(String childName) throws XMLDBException {
 666   
 667  129 if (null == childName || childName.length() == 0) {
 668  2 throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION,
 669    FaultCodes.COL_COLLECTION_NOT_FOUND,
 670    "Cannot remove child collection '" + childName + "': Name is empty");
 671    }
 672   
 673  127 checkOpen();
 674  127 try {
 675  127 Map params = new HashMap();
 676  127 params.put(RPCDefaultMessage.COLLECTION, collPath);
 677  127 params.put(RPCDefaultMessage.NAME, childName);
 678   
 679  127 String result = (String) runRemoteCommand("RemoveCollection", params);
 680  125 if (!result.equals("yes")) {
 681  0 throw new XMLDBException(ErrorCodes.INVALID_COLLECTION,
 682    "Cannot remove child collection '" + childName + "'");
 683    }
 684    } catch (XMLDBException x) {
 685  2 throw x; // propagate any xmldb exception.
 686    } catch (Exception e) {
 687  0 throw new XMLDBException(ErrorCodes.INVALID_COLLECTION,
 688    "Cannot remove child collection '" + childName + "'", e);
 689    }
 690    }
 691   
 692    /* see superclass for documentation */
 693  5 public String[] listIndexers() throws XMLDBException {
 694  5 checkOpen();
 695  5 try {
 696  5 Map params = new HashMap();
 697  5 params.put(RPCDefaultMessage.COLLECTION, collPath);
 698    // XML-RPC returns array of String objects
 699  5 Object[] result = (Object[]) runRemoteCommand("ListIndexers", params);
 700  5 String[] list = new String[result.length];
 701    //noinspection SuspiciousSystemArraycopy
 702  5 System.arraycopy(result, 0, list, 0, result.length);
 703   
 704  5 return list;
 705    } catch (Exception e) {
 706  0 throw FaultCodes.createXMLDBException(e);
 707    }
 708    }
 709   
 710    /* see superclass for documentation */
 711  30 public void createIndexer(Document configuration) throws XMLDBException {
 712  30 checkOpen();
 713  30 try {
 714  30 Map params = new HashMap();
 715  30 params.put(RPCDefaultMessage.COLLECTION, collPath);
 716  30 params.put(RPCDefaultMessage.CONFIGURATION, TextWriter.toString(configuration));
 717   
 718  29 runRemoteCommand("CreateIndexer", params);
 719    } catch (Exception e) {
 720  4 throw FaultCodes.createXMLDBException(e);
 721    }
 722    }
 723   
 724    /* see superclass for documentation */
 725  22 public void dropIndexer(String name) throws XMLDBException {
 726  22 checkOpen();
 727  22 try {
 728  22 Map params = new HashMap();
 729  22 params.put(RPCDefaultMessage.COLLECTION, collPath);
 730  22 params.put(RPCDefaultMessage.NAME, name);
 731   
 732  22 runRemoteCommand("RemoveIndexer", params);
 733    } catch (Exception e) {
 734  3 throw FaultCodes.createXMLDBException(e);
 735    }
 736    }
 737   
 738    /* see superclass for documentation */
 739  0 public void shutdown() throws XMLDBException {
 740  0 checkOpen();
 741  0 try {
 742  0 Map params = new HashMap();
 743   
 744  0 runRemoteCommand("Shutdown", params);
 745    } catch (Exception e) {
 746  0 throw FaultCodes.createXMLDBException(e);
 747    }
 748    }
 749   
 750  18 public MetaData getMetaData(String id) throws XMLDBException {
 751  18 checkOpen();
 752  18 try {
 753  18 Map params = new HashMap();
 754  18 params.put(RPCDefaultMessage.COLLECTION, collPath);
 755  18 if (id != null) {
 756  11 params.put(RPCDefaultMessage.NAME, id);
 757    }
 758  18 params.put(RPCDefaultMessage.COMPRESSED, "true");
 759   
 760  18 Object result = runRemoteCommand(id == null ? "GetCollectionMeta" : "GetDocumentMeta", params);
 761  18 Document metaDoc = DOMParser.toDocument(result.toString());
 762  18 MetaData meta = new MetaData(id);
 763  18 meta.streamFromXML(metaDoc.getDocumentElement(), true);
 764  18 return meta;
 765    } catch (Exception e) {
 766  0 throw FaultCodes.createXMLDBException(e);
 767    }
 768    }
 769   
 770  6 public void setMetaData(String id, MetaData meta) throws XMLDBException {
 771  6 checkOpen();
 772  6 try {
 773  6 Map params = new HashMap();
 774  6 params.put(RPCDefaultMessage.COLLECTION, collPath);
 775  6 if (id != null) {
 776  4 params.put(RPCDefaultMessage.NAME, id);
 777    }
 778  6 params.put(RPCDefaultMessage.COMPRESSED, "true");
 779   
 780  6 Document doc = new DocumentImpl();
 781  6 doc.appendChild(meta.streamToXML(doc, true));
 782  6 params.put(RPCDefaultMessage.META, TextWriter.toString(doc));
 783   
 784    // Object result =
 785  6 runRemoteCommand(id == null ? "SetCollectionMeta" : "SetDocumentMeta", params);
 786    // Document metaDoc = DOMParser.toDocument(result.toString());
 787    // meta = new MetaData(id);
 788    // meta.streamFromXML(metaDoc.getDocumentElement(), true);
 789    // return meta;
 790    } catch (Exception e) {
 791  0 throw FaultCodes.createXMLDBException(e);
 792    }
 793    }
 794    }