Clover coverage report -
Coverage timestamp: Sun Nov 16 2008 23:05:30 GMT
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  1128 public CollectionImpl(XmlRpcClient client, String collPath) throws XMLDBException {
 92  1128 super(collPath);
 93  1128 this.client = client;
 94   
 95    /* Just check the collection does actually exist */
 96  1128 Map params = new HashMap(3);
 97  1128 params.put(RPCDefaultMessage.COLLECTION, collPath);
 98  1128 String exists = (String) runRemoteCommand("GetCollectionConfiguration", params);
 99   
 100  1127 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  1126 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  2357 private Object runRemoteCommand(String cmdName, Map params) throws XMLDBException {
 129  2357 try {
 130  2357 params.put(RPCMessageInterface.MESSAGE_PARAM, cmdName);
 131  2357 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  175 public Resource getResource(String id) throws XMLDBException {
 162  175 checkOpen();
 163   
 164  175 try {
 165  175 if (id == null) {
 166  2 return null;
 167    }
 168   
 169  173 Map params = new HashMap();
 170  173 params.put(RPCDefaultMessage.COLLECTION, collPath);
 171  173 params.put(RPCDefaultMessage.NAME, id);
 172  173 params.put(RPCDefaultMessage.COMPRESSED, "true");
 173  173 params.put(RPCDefaultMessage.TIMESTAMP, Long.toString(symsDeserializer.getLastModified()));
 174   
 175  173 Object result = runRemoteCommand("GetResource", params);
 176   
 177  173 if (result == null) {
 178    // No resource found
 179  5 return null;
 180   
 181  168 } else if (result instanceof Map) {
 182    // Result is compressed XML.
 183  166 Map compressed = (Map) result;
 184  166 SymbolTable syms = symsDeserializer.getSymbols(compressed);
 185  166 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  406 public void storeResource(Resource res) throws XMLDBException {
 248   
 249  406 if (res.getContent() == null) {
 250  0 throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "no resource data");
 251    }
 252   
 253  406 checkOpen();
 254  406 try {
 255  406 Map params = createParams(res);
 256  406 String name = (String) runRemoteCommand("SetResource", params);
 257  406 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  410 private Map createParams(Resource res) throws XMLDBException {
 326  410 Map params = new HashMap();
 327  410 params.put(RPCDefaultMessage.COLLECTION, collPath);
 328  410 params.put(RPCDefaultMessage.NAME, res.getId());
 329  410 params.put(RPCDefaultMessage.DOCUMENT, res.getContent());
 330  410 return params;
 331    }
 332   
 333  408 private void seResourcetId(Resource res, String name) {
 334  408 if (res instanceof XMLResource) {
 335  404 ((XMLResourceImpl) res).setId(name);
 336    } else {
 337  4 ((BinaryResourceImpl) res).setId(name);
 338    }
 339    }
 340   
 341    /* see superclass for documentation */
 342  1622 public boolean isOpen() {
 343   
 344  1622 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  127 public Collection getChildCollection(String name) throws XMLDBException {
 368   
 369  127 if (name.indexOf('/') != -1) {
 370  1 throw new XMLDBException(ErrorCodes.INVALID_COLLECTION);
 371    }
 372   
 373  126 try {
 374  126 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  153 public void removeResource(Resource res) throws XMLDBException {
 472   
 473