Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 336   Methods: 8
NCLOC: 123   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
DatabaseImpl.java 46.7% 66.7% 87.5% 62.1%
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: DatabaseImpl.java 708000 2008-10-26 14:50:07Z natalia $
 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.CommonConfigurable;
 25    import org.apache.xmlrpc.client.XmlRpcClient;
 26    import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
 27    import org.xmldb.api.base.Collection;
 28    import org.xmldb.api.base.Database;
 29    import org.xmldb.api.base.ErrorCodes;
 30    import org.xmldb.api.base.XMLDBException;
 31   
 32    import java.net.MalformedURLException;
 33    import java.net.URL;
 34    import java.util.HashMap;
 35    import java.util.Map;
 36   
 37    /**
 38    * Implements XML:DB's <code>Database</code> interface using XML-RPC to communicate
 39    * with the Xindice XML-RPC server. Usually, this class is not used
 40    * directly, but {@link org.apache.xindice.client.xmldb.DatabaseImpl} is used instead.
 41    *
 42    * Note this class is a database <em>driver</em>, and one class of this database
 43    * could be used to connect to <em>many</em> different databases, on one or
 44    * different Xindice XML-RPC servers.
 45    *
 46    * XML-RPC database driver uses following configuration parameters:
 47    * <ul>
 48    * <li><strong>service-location</strong>: Specifies path to the Xindice server WebApp</li>
 49    * <li><strong>xmlrpc-driver</strong>: Specifies name of the SAX parser to use</li>
 50    * <li><strong>xmlrpc-user</strong>: If server protected with HTTP Basic Auth, specifies user name</li>
 51    * <li><strong>xmlrpc-password</strong>: If server protected with HTTP Basic Auth, specifies password</li>
 52    * </ul>
 53    *
 54    * If one of these parameters is not specified, system properties will be used.
 55    *
 56    * @author <a href="mailto:james.bates@amplexor.com">James Bates</a>
 57    * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
 58    * @version $Revision: 708000 $, $Date: 2008-10-26 14:50:07 +0000 (Sun, 26 Oct 2008) $
 59    */
 60    public class DatabaseImpl extends CommonConfigurable implements Database {
 61   
 62    private static final Log log = LogFactory.getLog(DatabaseImpl.class);
 63   
 64    /**
 65    * Driver property name for the xml-rpc service location.
 66    */
 67    public static final String PROP_SERVICE_LOCATION = "service-location";
 68   
 69    /**
 70    * System property name for the service location
 71    */
 72    public static final String SYSPROP_SERVICE_LOCATION = "xindice.xmlrpc.service-location";
 73   
 74    /**
 75    * Driver property name for the basic authentication user name.
 76    */
 77    public static final String PROP_XMLRPC_USER = "xmlrpc-user";
 78   
 79    /**
 80    * System property name for the basic authentication user name in case
 81    * there were no configuration property passed
 82    */
 83    public static final String SYSPROP_XMLRPC_USER = "xindice.xmlrpc.user";
 84   
 85    /**
 86    * Driver property name for the basic authentication password.
 87    */
 88    public static final String PROP_XMLRPC_PASSWORD = "xmlrpc-password";
 89   
 90    /**
 91    * System property name for the basic authentication password in case
 92    * there were no configuration property passed
 93    */
 94    public static final String SYSPROP_XMLRPC_PASSWORD = "xindice.xmlrpc.password";
 95   
 96    /**
 97    * Default path to the XML-RPC service in the web server
 98    */
 99    private static final String DEFAULT_SERVICE_LOCATION = "/xindice/";
 100   
 101    /**
 102    * Prefix used to denote XML:DB URI's that should use this driver
 103    */
 104    public static final String DRIVER_NAME = "xindice";
 105   
 106    /**
 107    * XML:DB conformance level of this driver
 108    */
 109    private static final String CONFORMANCE_LEVEL = "0";
 110   
 111    /**
 112    * Map contains XML_RPC clients for different hostname:port addresses
 113    */
 114    private Map clients = new HashMap();
 115   
 116   
 117    /**
 118    * Create a new DatabaseImpl object.
 119    */
 120  1 public DatabaseImpl() {
 121  1 super();
 122    }
 123   
 124    /**
 125    * Create a new DatabaseImpl object with a copy of the properties
 126    * from the DatabaseImpl parameter.
 127    *
 128    * @param config from which the initial parameters for this
 129    * DatabaseImpl object are copied.
 130    */
 131  1 public DatabaseImpl(CommonConfigurable config) {
 132  1 super(config);
 133    }
 134   
 135    /**
 136    * Determines whether this <code>Database</code> implementation can handle
 137    * the URI. It should return true if the Database instance knows how to
 138    * handle the URI and false otherwise.
 139    *
 140    * @param uri the URI to check for.
 141    * @return true if the URI can be handled, false otherwise.
 142    * @exception XMLDBException with expected error codes.<br />
 143    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 144    * specific errors that occur.<br />
 145    * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br />
 146    */
 147  1013 public boolean acceptsURI(String uri) throws XMLDBException {
 148   
 149  1013 return ((uri != null) && uri.startsWith(getName() + "://"));
 150    }
 151   
 152    /**
 153    * Initializes XML-RPC service location, basic authentication.
 154    * Creates XML-RPC client.
 155    *
 156    * @param hostPort of the Xindice XML-RPC server
 157    * @return XML-RPC client connected to the Xindice server
 158    * @throws XMLDBException if host or port information was incorrect and resulted in malformed URL
 159    */
 160  1010 private XmlRpcClient connect(String hostPort) throws XMLDBException {
 161   
 162  1010 synchronized (this) {
 163  1010 if (!clients.containsKey(hostPort)) {
 164    // Initialize XML-RPC static properties
 165  1 XmlRpcClientConfigImpl cfg = new XmlRpcClientConfigImpl();
 166  1 cfg.setEncoding("utf-8");
 167    // FIXME Is XmlRpc.setKeepAlive(true); gone forever?
 168   
 169    /*
 170    * Determine the path in the web server to the XML-RPC service.
 171    *
 172    * In priority order:
 173    * DatabaseImpl service-location property
 174    * (passed in the serviceLocation parameter)
 175    * System property "xindice.xmlrpc.service-location"
 176    * Default value "/xindice/"
 177    */
 178  1 String serviceLocation = getProperty(PROP_SERVICE_LOCATION);
 179  1 if (serviceLocation == null) {
 180  0 serviceLocation = System.getProperty(SYSPROP_SERVICE_LOCATION);
 181  0 if (serviceLocation == null) {
 182  0 serviceLocation = DEFAULT_SERVICE_LOCATION;
 183    }
 184    }
 185  1 if (!serviceLocation.startsWith("/")) {
 186  0 serviceLocation = "/" + serviceLocation;
 187    }
 188  1 if (!serviceLocation.endsWith("/")) {
 189  0 serviceLocation = serviceLocation + "/";
 190    }
 191   
 192  1 String xmlRpcURL = "http://" + hostPort + serviceLocation;
 193  1 if (log.isDebugEnabled()) {
 194  0 log.debug("Using URL: '" + xmlRpcURL + "'");
 195    }
 196  1 try {
 197  1 cfg.setServerURL(new URL(xmlRpcURL));
 198    } catch (MalformedURLException e) {
 199  0 throw new XMLDBException(ErrorCodes.INVALID_URI, e);
 200    }
 201   
 202    /*
 203    * Determine basic authentication parameters
 204    */
 205  1 String basicUser = getProperty(PROP_XMLRPC_USER);
 206  1 if (basicUser == null) {
 207  1 basicUser = System.getProperty(SYSPROP_XMLRPC_USER);
 208    }
 209  1 if (basicUser != null) {
 210  0 String basicPassword = getProperty(PROP_XMLRPC_PASSWORD);
 211  0 if (basicPassword == null) {
 212  0 basicPassword = System.getProperty(SYSPROP_XMLRPC_PASSWORD);
 213    }
 214   
 215  0 if (log.isDebugEnabled()) {
 216  0 log.debug("Using Basic authentication. User: '" + basicUser + "', password: '" + basicPassword + "'");
 217    }
 218  0 cfg.setBasicUserName(basicPassword);
 219  0 cfg.setBasicPassword(basicPassword);
 220    }
 221   
 222  1 XmlRpcClient client = new XmlRpcClient();
 223  1 client.setConfig(cfg);
 224   
 225  1 clients.put(hostPort, client);
 226    }
 227    }
 228   
 229  1010 return (XmlRpcClient) clients.get(hostPort);
 230    }
 231   
 232    /**
 233    * Retrieves a <code>Collection</code> instance based on the URI provided
 234    * in the <code>uri</code> parameter. The format of the URI is defined in the
 235    * documentation for DatabaseManager.getCollection().<p/>
 236    *
 237    * Authentication is handled via username and password however it is not
 238    * required that the database support authentication. Databases that do not
 239    * support authentication MUST ignore the
 240    * <code>username</code> and <code>password</code> if those provided are not
 241    * null.
 242    *
 243    * @param uri the URI to use to locate the collection.
 244    * @param password The password to use for authentication to the database or
 245    * null if the database does not support authentication.
 246    * @return A <code>Collection</code> instance for the requested collection or
 247    * null if the collection could not be found.
 248    * @exception XMLDBException with expected error codes.<br />
 249    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 250    * specific errors that occur.<br />
 251    * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br />
 252    * <code>ErrroCodes.PERMISSION_DENIED</code> If the <code>username</code>
 253    * and <code>password</code> were not accepted by the database.
 254    */
 255  1010 public Collection getCollection(String uri, String userName, String password) throws XMLDBException {
 256    /* TODO: introduce authentication some day */
 257   
 258  1010 if (!acceptsURI(uri)) {
 259  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 260    "Invalid URL: " + uri);
 261    }
 262   
 263    /* Chop off driver prefix, and '://' */
 264  1010 uri = uri.substring(getName().length() + 3);
 265   
 266    /* Extract host name & port, if present */
 267  1010 int firstSlash = uri.indexOf('/');
 268  1010 if (firstSlash == -1) {
 269  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 270    "Invalid URL (must have '/'): " + uri);
 271    }
 272   
 273    /* Extract collection name */
 274  1010 String collPath = uri.substring(firstSlash);
 275  1010 if (!collPath.startsWith("/")) {
 276  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 277    "Invalid URL (collection name must start with '/'): " + uri);
 278    }
 279   
 280  1010 String hostPort = uri.substring(0, firstSlash);
 281   
 282    /* Absent host defaults to localhost and standard Xindice HTTP port */
 283  1010 if (hostPort.equals("")) {
 284  0 hostPort = "127.0.0.1:8888";
 285    }
 286   
 287  1010 try {
 288  1010 return new CollectionImpl(connect(hostPort), collPath);
 289    } catch (XMLDBException e) {
 290  2 if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
 291    // per getCollection contract, return null if not found
 292  1 return null;
 293    }
 294  1 throw e;
 295    }
 296    }
 297   
 298    /**
 299    * Returns the prefix used in XML:DB to denote URI's that this driver can
 300    * handle.
 301    *
 302    * @return the prefix driver name
 303    * @exception XMLDBException with expected error codes.<br />
 304    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 305    * specific errors that occur.<br />
 306    */
 307  2027 public String getName() throws XMLDBException {
 308  2027 return DRIVER_NAME;
 309    }
 310   
 311    /**
 312    * Returns an array of names associated with the Database instance.
 313    *
 314    * @return the array of name of the object.
 315    * @exception XMLDBException with expected error codes.<br />
 316    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 317    * specific errors that occur.<br />
 318    */
 319  2 public String[] getNames() throws XMLDBException {
 320  2 return new String[]{ getName() };
 321    }
 322   
 323    /**
 324    * Returns the XML:DB API Conformance level for the implementation. This can
 325    * be used by client programs to determine what functionality is available to
 326    * them.
 327    *
 328    * @return the XML:DB API conformance level for this implementation.
 329    * @exception XMLDBException with expected error codes.<br />
 330    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 331    * specific errors that occur.<br />
 332    */
 333  0 public String getConformanceLevel() throws XMLDBException {
 334  0 return CONFORMANCE_LEVEL;
 335    }
 336    }