Clover coverage report -
Coverage timestamp: Sun Nov 1 2009 23:08:24 UTC
file stats: LOC: 457   Methods: 11
NCLOC: 208   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
DatabaseImpl.java 60.4% 71.4% 72.7% 68.2%
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 564998 2007-08-12 02:51:49Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.client.xmldb.embed;
 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.xindice.core.DBException;
 26    import org.apache.xindice.core.Database;
 27    import org.apache.xindice.core.FaultCodes;
 28    import org.apache.xindice.server.Xindice;
 29    import org.apache.xindice.util.Configuration;
 30    import org.apache.xindice.util.ReadOnlyException;
 31    import org.apache.xindice.util.XindiceException;
 32    import org.apache.xindice.util.XindiceRuntimeException;
 33    import org.apache.xindice.xml.dom.DOMParser;
 34   
 35    import org.xmldb.api.base.Collection;
 36    import org.xmldb.api.base.ErrorCodes;
 37    import org.xmldb.api.base.XMLDBException;
 38   
 39    import java.io.File;
 40    import java.io.FileInputStream;
 41    import java.io.FileNotFoundException;
 42    import java.io.IOException;
 43   
 44    /**
 45    * Implements XML:DB's <code>Database</code> interface providing embedded access to
 46    * a Xindice database. Usually, this class is not used
 47    * directly, but {@link org.apache.xindice.client.xmldb.DatabaseImpl} is used instead.
 48    *
 49    * Embedded database driver supports multiple database instances simultaneously. To use
 50    * multiple instances, configure several <code>root-collection</code> elements in the
 51    * Xindice configuration file and point this driver to this configuration.
 52    *
 53    * Currently, databases are loaded by the driver lazily, on first access to the
 54    * database.
 55    *
 56    * Embedded database driver uses following configuration parameters:
 57    * <ul>
 58    * <li><strong>db-home</strong>: Specifies path to the Xindice DB Home directory</li>
 59    * <li><strong>configuration</strong>: Specifies path to the Xindice Configuration file</li>
 60    * <li><strong>managed</strong>: If set to true, driver will not load databases by himself,
 61    * but will assume that third party code had initialized the database
 62    * (See {@link org.apache.xindice.client.xmldb.managed.DatabaseImpl})</li>
 63    * </ul>
 64    *
 65    * If one of these parameters is not specified, system properties will be used.
 66    *
 67    * Driver parameters can be specified either:
 68    * <ul>
 69    * <li>by instantiating {@link org.apache.xindice.client.xmldb.DatabaseImpl}
 70    * and setting its properties (see {@link #setProperty}). You won't use this class
 71    * directly in this case.</li>
 72    * <li>by setting parameters via {@link #setProperty} method <strong>before</strong>
 73    * obtaining collection with {@link #getCollection(String, String, String)} method.</li>
 74    * <li>by setting Java system properties (see {@link System#setProperty(String, String)})</li>
 75    * <li>by using constructor {@link DatabaseImpl#DatabaseImpl(CommonConfigurable)} and
 76    * passing instance of CommonConfigurable with all properties already set.</li>
 77    *
 78    * @author <a href="mailto:kstaken@xmldatabases.org">Kimbro Staken</a>
 79    * @author <a href="mailto:james.bates@amplexor.com">James Bates</a>
 80    * @author <a href="mailto:vladimir@apache.org">Vladimir R. Bossicard</a>
 81    * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
 82    * @version $Revision: 564998 $, $Date: 2007-08-11 19:51:49 -0700 (Sat, 11 Aug 2007) $
 83    */
 84    public class DatabaseImpl extends CommonConfigurable implements org.xmldb.api.base.Database {
 85   
 86    private static final Log log = LogFactory.getLog(DatabaseImpl.class);
 87   
 88    /**
 89    * Driver property name for the xindice database home.
 90    */
 91    public static final String PROP_XINDICE_DB_HOME = "db-home";
 92   
 93    /**
 94    * System property name for the xindice database home.
 95    */
 96    public static final String SYSPROP_XINDICE_DB_HOME = Xindice.PROP_XINDICE_DB_HOME;
 97   
 98    /**
 99    * Driver property name for the xindice configuration file.
 100    */
 101    public static final String PROP_XINDICE_CONFIGURATION = "configuration";
 102   
 103    /**
 104    * System property name for the xindice configuration file.
 105    */
 106    public static final String SYSPROP_XINDICE_CONFIGURATION = Xindice.PROP_XINDICE_CONFIGURATION;
 107   
 108    /**
 109    * Driver property name for the managed configuration parameter.
 110    */
 111    public static final String PROP_XINDICE_MANAGED = "db-managed";
 112   
 113    /**
 114    * System property name for the managed configuration parameter.
 115    */
 116    public static final String SYSPROP_XINDICE_MANAGED = "xindice.db.managed";
 117   
 118    /**
 119    * Prefix used to denote XML:DB URI's that should use this driver
 120    */
 121    public static final String DRIVER_NAME = "xindice-embed";
 122   
 123    /**
 124    * XML:DB conformance level of this driver
 125    */
 126    private static final String CONFORMANCE_LEVEL = "0";
 127   
 128   
 129    /**
 130    * Database configuration location.
 131    * Null if built-in configuration was used.
 132    */
 133    private String configFile;
 134   
 135   
 136    /**
 137    * Creates new <code>DatabaseImpl</code> instance.
 138    */
 139  0 public DatabaseImpl() {
 140    }
 141   
 142    /**
 143    * Create a new DatabaseImpl object with a copy of the properties
 144    * from the <code>config</code> parameter.
 145    *
 146    * This allows to pass properties such as PROP_XINDICE_CONFIGURATION
 147    * to this instance. Usually this is done by instantiating not this
 148    * class, but {@link org.apache.xindice.client.xmldb.DatabaseImpl},
 149    * set all the necessary parameters, and then get a collection.
 150    *
 151    * @param config from which the initial parameters for this
 152    * DatabaseImpl object are copied.
 153    */
 154  3 public DatabaseImpl(CommonConfigurable config) {
 155  3 super(config);
 156    }
 157   
 158    /**
 159    * Get named database instance. If database is not loaded yet, load it.
 160    *
 161    * @param dbName Database name
 162    * @param uri Database URI
 163    * @return Database instance
 164    * @throws XMLDBException if configuration for this database is not found,
 165    * or could not be read.
 166    */
 167  2020 private Database getDatabase(String dbName, String uri) throws XMLDBException {
 168  2020 Database database = Database.getDatabase(dbName);
 169  2020 if (database == null && !isManaged()) {
 170  4 Configuration dbConfig = getConfiguration(dbName);
 171  4 if (log.isDebugEnabled()) {
 172  0 log.debug("Mounting database: '" + dbName + "'");
 173    }
 174   
 175  4 if (dbConfig == null) {
 176  2 throw new XMLDBException(ErrorCodes.NO_SUCH_DATABASE,
 177    "Database '" + dbName + "' not found: " + uri);
 178    }
 179   
 180  2 try {
 181  2 database = Database.getDatabase(dbConfig);
 182    } catch (DBException e) {
 183  0 throw FaultCodes.createXMLDBException(e);
 184    }
 185  2 if (log.isDebugEnabled()) {
 186  0 log.info("Mounted database: '" + database.getName() + "'");
 187    }
 188    }
 189   
 190  2018 return database;
 191    }
 192   
 193  4 protected boolean isManaged() {
 194  4 String managed = null;
 195  4 try {
 196    // Try configuration first
 197  4 managed = getProperty(PROP_XINDICE_MANAGED);
 198    } catch (XMLDBException e) {
 199    /* ignored */
 200    }
 201  4 if (managed == null) {
 202    // Fallback to system property
 203  4 managed = System.getProperty(SYSPROP_XINDICE_MANAGED);
 204    }
 205  4 return Boolean.valueOf(managed).booleanValue();
 206    }
 207   
 208    /**
 209    * Get configuration for the named database
 210    *
 211    * @param dbName Database name
 212    * @return Database configuration
 213    * @throws XMLDBException if unable to load database configuration
 214    */
 215  4 protected Configuration getConfiguration(String dbName) throws XMLDBException {
 216  4 Configuration config = loadConfiguration();
 217   
 218    // Find database config with the given database name
 219  4 Configuration[] roots = config.getChildren("root-collection");
 220  4 config = null;
 221  4 for (int i = 0; i < roots.length; i++) {
 222  4 if (dbName.equals(roots[i].getAttribute(Database.NAME))) {
 223  2 config = roots[i];
 224  2 break;
 225    }
 226    }
 227   
 228  4 if (config == null) {
 229  2 return null;
 230    }
 231   
 232    // Figure out database root
 233  2 String dbRoot = config.getAttribute(Database.DBROOT, Database.DBROOT_DEFAULT);
 234  2 if (!new File(dbRoot).isAbsolute()) {
 235    // Let's see if the XINDICE_DB_HOME property was specified.
 236  2 String home = null;
 237  2 try {
 238    // Try configuration first
 239  2 home = getProperty(PROP_XINDICE_DB_HOME);
 240    } catch (XMLDBException e) {
 241    /* ignored */
 242    }
 243  2 if (home == null) {
 244    // Fallback to system property
 245  2 home = System.getProperty(SYSPROP_XINDICE_DB_HOME);
 246    }
 247   
 248  2 if (home != null) {
 249  2 try {
 250  2 dbRoot = new File(home + File.separator + dbRoot).getCanonicalPath();
 251    } catch (IOException e) {
 252  0 log.warn("getCanonicalPath failed", e);
 253  0 dbRoot = new File(home + File.separator + dbRoot).getAbsolutePath();
 254    }
 255  0 } else if (configFile != null) {
 256  0 dbRoot = new File(configFile).getAbsoluteFile().getParent() + File.separator + dbRoot;
 257    } else {
 258  0 log.warn("The database configuration file is not specified and there was no "
 259    + SYSPROP_XINDICE_DB_HOME + " property set, "
 260    + "so Xindice was unable to determine a database location. "
 261    + "Database will be created relative to the current directory.");
 262  0 try {
 263  0 dbRoot = new File("." + File.separator + dbRoot).getCanonicalPath();
 264    } catch (IOException e) {
 265  0 log.warn("getCanonicalPath failed", e);
 266  0 dbRoot = new File("." + File.separator + dbRoot).getAbsolutePath();
 267    }
 268    }
 269  2 try {
 270  2 config.setAttribute(Database.DBROOT, dbRoot);
 271    } catch (ReadOnlyException e) {
 272    /* ignored */
 273    }
 274    }
 275   
 276  2 return config;
 277    }
 278   
 279  4 private Configuration loadConfiguration() throws XMLDBException {
 280  4 Configuration config;
 281   
 282  4 if (configFile == null) {
 283  2 try {
 284    // Try configuration first
 285  2 configFile = getProperty(PROP_XINDICE_CONFIGURATION);
 286    } catch (XMLDBException e) {
 287    /* ignored */
 288    }
 289  2 if (configFile == null) {
 290    // Fallback to system property
 291  2 configFile = System.getProperty(SYSPROP_XINDICE_CONFIGURATION);
 292    }
 293  2 if ("".equals(configFile)) {
 294  0 configFile = null;
 295    }
 296    }
 297   
 298  4 if (configFile != null) {
 299  4 if (log.isInfoEnabled()) {
 300  0 log.info("Specified configuration file: '" + configFile + "'");
 301    }
 302  4 try {
 303  4 FileInputStream configXMLFile = new FileInputStream(configFile);
 304  4 config = new Configuration(DOMParser.toDocument(configXMLFile), false);
 305    } catch (XindiceException e) {
 306  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR,
 307    "Unable to parse database configuration file: " + configFile, e);
 308    } catch (FileNotFoundException e) {
 309  0 throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR,
 310    "Database configuration file not found: " + configFile);
 311    }
 312    } else {
 313  0 if (log.isInfoEnabled()) {
 314  0 log.info("No configuration file specified, going with the default configuration");
 315    }
 316   
 317  0 try {
 318  0 config = new Configuration(DOMParser.toDocument(Xindice.DEFAULT_CONFIGURATION), false);
 319    } catch (XindiceException e) {
 320  0 throw new XindiceRuntimeException("Unable to parse default database configuration", e);
 321    }
 322    }
 323   
 324  4 return config;
 325    }
 326   
 327    /**
 328    * Determines whether this <code>Database</code> implementation can handle
 329    * the URI. It should return true if the Database instance knows how to
 330    * handle the URI and false otherwise.
 331    *
 332    * @param uri the URI to check for.
 333    * @return true if the URI can be handled, false otherwise.
 334    * @exception XMLDBException with expected error codes.<br />
 335    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 336    * specific errors that occur.<br />
 337    * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br />
 338    */
 339  2023 public boolean acceptsURI(String uri) throws XMLDBException {
 340  2023 return ((uri != null) && uri.startsWith(getName() + "://"));
 341    }
 342   
 343    /**
 344    * Retrieves a <code>Collection</code> instance based on the URI provided
 345    * in the <code>uri</code> parameter. The format of the URI is defined in the
 346    * documentation for DatabaseManager.getCollection().<p/>
 347    *
 348    * Authentication is handled via username and password however it is not
 349    * required that the database support authentication. Databases that do not
 350    * support authentication MUST ignore the
 351    * <code>username</code> and <code>password</code> if those provided are not
 352    * null.
 353    *
 354    * @param uri the URI to use to locate the collection.
 355    * @param password The password to use for authentication to the database or
 356    * null if the database does not support authentication.
 357    * @return A <code>Collection</code> instance for the requested collection or
 358    * null if the collection could not be found.
 359    * @exception XMLDBException with expected error codes.<br />
 360    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 361    * specific errors that occur.<br />
 362    * <code>ErrroCodes.INVALID_URI</code> If the URI is not in a valid format. <br />
 363    * <code>ErrroCodes.PERMISSION_DENIED</code> If the <code>username</code>
 364    * and <code>password</code> were not accepted by the database.
 365    */
 366  2020 public Collection getCollection(String uri, String userName, String password) throws XMLDBException {
 367    /* TODO: introduce authentication some day */
 368   
 369  2020 if (!acceptsURI(uri)) {
 370  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 371    "Invalid URL: " + uri);
 372    }
 373   
 374    /* Chop off driver prefix, and '://' */
 375  2020 uri = uri.substring(getName().length() + 3);
 376   
 377    /* Extract host name & port, if present */
 378  2020 int firstSlash = uri.indexOf('/');
 379  2020 if (firstSlash == -1) {
 380  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 381    "Invalid URL (must have '/'): " + uri);
 382    }
 383   
 384    /* Extract collection name */
 385  2020 String collPath = uri.substring(firstSlash);
 386  2020 if (!collPath.startsWith("/")) {
 387  0 throw new XMLDBException(ErrorCodes.INVALID_URI,
 388    "Invalid URL (collection name must start with '/'): " + uri);
 389    }
 390   
 391    // Find the database name. We just skip the first slash
 392  2020 int colIndex = collPath.indexOf('/', 1);
 393   
 394    // We assume there's no collection specified
 395  2020 String dbName = collPath.substring(1);
 396   
 397    // If colIndex isn't -1 then we need to pick out the db name
 398  2020 if (colIndex != -1) {
 399  2010 dbName = collPath.substring(1, colIndex);
 400    }
 401   
 402  2020 Database database = getDatabase(dbName, uri);
 403  2018 if (database == null) {
 404  0 throw new XMLDBException(ErrorCodes.NO_SUCH_DATABASE,
 405    "Database '" + dbName + "' not found. URL: " + uri);
 406    }
 407   
 408  2018 try {
 409  2018 return new CollectionImpl(database, collPath);
 410    } catch (XMLDBException e) {
 411  2 if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) {
 412    // Per getCollection contract, return null if not found
 413  2 return null;
 414    }
 415  0 throw e;
 416    }
 417    }
 418   
 419    /**
 420    * Returns the prefix used in XML:DB to denote URI's that this driver can
 421    * handle.
 422    *
 423    * @return the prefix driver name
 424    * @exception XMLDBException with expected error codes.<br />
 425    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 426    * specific errors that occur.<br />
 427    */
 428  4043 public String getName() throws XMLDBException {
 429  4043 return DRIVER_NAME;
 430    }
 431   
 432    /**
 433    * Returns an array of names associated with the Database instance.
 434    *
 435    * @return the array of name of the object.
 436    * @exception XMLDBException with expected error codes.<br />
 437    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 438    * specific errors that occur.<br />
 439    */
 440  0 public String[] getNames() throws XMLDBException {
 441  0 return new String[]{ getName() };
 442    }
 443   
 444    /**
 445    * Returns the XML:DB API Conformance level for the implementation. This can
 446    * be used by client programs to determine what functionality is available to
 447    * them.
 448    *
 449    * @return the XML:DB API conformance level for this implementation.
 450    * @exception XMLDBException with expected error codes.<br />
 451    * <code>ErrorCodes.VENDOR_ERROR</code> for any vendor
 452    * specific errors that occur.<br />
 453    */
 454  0 public String getConformanceLevel() throws XMLDBException {
 455  0 return CONFORMANCE_LEVEL;
 456    }
 457    }