vgritsenko 2004/02/06 05:05:56
Modified: . status.xml config system.xml java/src/org/apache/xindice/client/xmldb/embed DatabaseImpl.java Log: Support multiple databases (embed driver only) Revision Changes Path 1.26 +6 -1 xml-xindice/status.xml Index: status.xml =================================================================== RCS file: /home/cvs/xml-xindice/status.xml,v retrieving revision 1.25 retrieving revision 1.26 diff -u -r1.25 -r1.26 --- status.xml 2 Feb 2004 14:36:19 -0000 1.25 +++ status.xml 6 Feb 2004 13:05:56 -0000 1.26 @@ -59,7 +59,12 @@ </todo> <changes> - <release version="1.1b4-dev" date="February 2 2004"> + <release version="1.1b4-dev" date="February 6 2004"> + <action dev="VG" type="update"> + Embedded driver supports two configuration variables (see javadoc + for details), and multiple database instances (see system.xml for an + example). + </action> <action dev="VG" type="update"> Add max-descriptors configuration attribute to all Filer implementations based on Paged. Default value is 16 file descriptors (same as before). 1.15 +19 -5 xml-xindice/config/system.xml Index: system.xml =================================================================== RCS file: /home/cvs/xml-xindice/config/system.xml,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- system.xml 30 Dec 2003 13:07:38 -0000 1.14 +++ system.xml 6 Feb 2004 13:05:56 -0000 1.15 @@ -9,12 +9,13 @@ <xindice> <!-- - - Defines the database instance. This release supports only one - - database instance. Attributes: + - Defines the database instance. Xindice server in servlet mode currently + - supports only one database instance. Attributes: - dbroot: - - Identifies location of the database. + - Identifies location of the database. Relative paths will be + - resolved depending on mode of operation. - name: - - Name of the database instance, + - Name of the database instance. - use-metadata: - When set to 'on', enables external metadata facilities - for this database instance. @@ -30,6 +31,19 @@ </filer> --> + <!-- + - Query Engine Configuration. + --> + <queryengine> + <resolver autoindex="false" class="org.apache.xindice.core.query.XPathQueryResolver"/> + <resolver class="org.apache.xindice.core.xupdate.XUpdateQueryResolver"/> + </queryengine> + </root-collection> + + <!-- + - Embedded driver suuports several DB instances. + --> + <root-collection dbroot="./db2/" name="db2" use-metadata="off"> <queryengine> <resolver autoindex="false" class="org.apache.xindice.core.query.XPathQueryResolver"/> <resolver class="org.apache.xindice.core.xupdate.XUpdateQueryResolver"/> 1.23 +169 -75 xml-xindice/java/src/org/apache/xindice/client/xmldb/embed/DatabaseImpl.java Index: DatabaseImpl.java =================================================================== RCS file: /home/cvs/xml-xindice/java/src/org/apache/xindice/client/xmldb/embed/DatabaseImpl.java,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- DatabaseImpl.java 22 Jan 2004 03:23:37 -0000 1.22 +++ DatabaseImpl.java 6 Feb 2004 13:05:56 -0000 1.23 @@ -66,6 +66,8 @@ import org.apache.xindice.server.Xindice; import org.apache.xindice.util.Configuration; import org.apache.xindice.util.XindiceException; +import org.apache.xindice.util.XindiceRuntimeException; +import org.apache.xindice.util.ReadOnlyException; import org.apache.xindice.xml.dom.DOMParser; import org.xmldb.api.base.Collection; @@ -75,14 +77,41 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.FileNotFoundException; /** - * implements XML:DB's <code>Database</code> interface providing - * embedded access to a Xindice database. + * Implements XML:DB's <code>Database</code> interface providing embedded access to + * a Xindice database. Usually, this class is not used + * directly, use [EMAIL PROTECTED] org.apache.xindice.client.xmldb.DatabaseImpl} instead. + * + * Embedded database driver supports multiple database instances simultaneously. To use + * multiple instances, use several instances of this driver with different values for + * the Xindice DB Name property. + * + * Embedded database driver uses following configuration parameters: + * <ul> + * <li>Xindice DB Home</li> + * <li>Xindice Configuration</li> + * </ul> + * + * These parameters can be specified either: + * <ul> + * <li>by instantiating [EMAIL PROTECTED] org.apache.xindice.client.xmldb.DatabaseImpl} + * and setting its properties (see [EMAIL PROTECTED] #setProperty}). You don't need to use + * this class directly in this case.</li> + * <li>by setting Java system properties (see [EMAIL PROTECTED] System#setProperty(String, String)})</li> + * <li>by using constructor [EMAIL PROTECTED] DatabaseImpl#DatabaseImpl(CommonConfigurable)} and + * passing instance of CommonConfigurable with all properties already set.</li> + * <li>by using [EMAIL PROTECTED] #setProperty} <strong>before</strong> calling + * [EMAIL PROTECTED] #getCollection(String, String, String)}.</li> + * + * These properties are used in the getCollection method, and changes in those properties will + * not affect this instance of the driver after collection is obtained. * * @author <a href="mailto:[EMAIL PROTECTED]">Kimbro Staken</a> * @author <a href="mailto:[EMAIL PROTECTED]">James Bates</a> * @author <a href="mailto:[EMAIL PROTECTED]">Vladimir R. Bossicard</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a> * @version CVS $Revision$, $Date$ */ public class DatabaseImpl extends CommonConfigurable implements org.xmldb.api.base.Database { @@ -90,6 +119,26 @@ private static final Log log = LogFactory.getLog(DatabaseImpl.class); /** + * Driver property name for the xindice database home. + */ + private static final String PROP_XINDICE_DB_HOME = "db-home"; + + /** + * System property name for the xindice database home. + */ + private static final String SYSPROP_XINDICE_DB_HOME = Xindice.PROP_XINDICE_DB_HOME; + + /** + * Driver property name for the xindice configuration file. + */ + private static final String PROP_XINDICE_CONFIGURATION = "configuration"; + + /** + * Driver property name for the xindice configuration file. + */ + private static final String SYSPROP_XINDICE_CONFIGURATION = Xindice.PROP_XINDICE_CONFIGURATION; + + /** * Prefix used to denote XML:DB URI's that should use this driver */ public static final String DRIVER_NAME = "xindice-embed"; @@ -99,18 +148,18 @@ */ private static final String CONFORMANCE_LEVEL = "0"; + /** - * Database instance + * Database configuration location. + * Null if built-in configuration was used. */ - private Database database; + private String configFile; + /** - * Creates new <code>DatabaseImpl</code> instance. The configuration is - * loaded from the file defined in the PROP_XINDICE_CONFIGURATION system - * variable. + * Creates new <code>DatabaseImpl</code> instance. */ - public DatabaseImpl() throws IOException, XindiceException { - init(); + public DatabaseImpl() { } /** @@ -119,101 +168,151 @@ * * This allows to pass properties such as PROP_XINDICE_CONFIGURATION * to this instance. Usually this is done by instantiating not this - * class, but <code>org.apache.xindice.client.xmldb.DatabaseImpl</code>, + * class, but [EMAIL PROTECTED] org.apache.xindice.client.xmldb.DatabaseImpl}, * set all the necessary parameters, and then get a collection. * * @param config from which the initial parameters for this * DatabaseImpl object are copied. */ - public DatabaseImpl(CommonConfigurable config) throws IOException, XindiceException { + public DatabaseImpl(CommonConfigurable config) { super(config); - init(); } /** - * Init this database instance. - * - * This is only a temporarly solution since the question of a new init - * method is raised in the xmldb:api group. Another solution could be to - * use the Configurable interface and only create the database when the - * getCollection method is called. + * Get named database instance. If database is not loaded yet, load it. * - * @throws IOException - * @throws XindiceException + * @throws XMLDBException if configuration for this database is not found, + * or could not be read. */ - private void init() throws IOException, XindiceException { - Configuration config = loadConfiguration(); + private Database getDatabase(String dbName, String uri) throws XMLDBException { + Database database = Database.getDatabase(dbName); + if (database == null) { + Configuration dbConfig = getConfiguration(dbName); + if (log.isDebugEnabled()) { + log.debug("Mounting database: '" + dbName + "'"); + } - this.database = Database.getDatabase(config); - if (null == this.database) { - log.fatal("Unable to configure database"); - throw new XindiceException("Unable to configure database"); - } + if (dbConfig == null) { + throw new XMLDBException(ErrorCodes.NO_SUCH_DATABASE, + "Database '" + dbName + "' not found: " + uri); + } - if (log.isDebugEnabled()) { - log.info("Database name: '" + this.database.getName() + "'"); + database = Database.getDatabase(dbConfig); + if (log.isDebugEnabled()) { + log.info("Mounted database: '" + database.getName() + "'"); + } } - } - - protected Configuration loadConfiguration() throws IOException, XindiceException { - Configuration config; - String configDir = null; - String configFile = null; - try { - // Try configuration first - configFile = getProperty(Xindice.PROP_XINDICE_CONFIGURATION); - } catch (XMLDBException ignored) { - } - if (configFile == null) { - // Fallback to system property - configFile = System.getProperty(Xindice.PROP_XINDICE_CONFIGURATION); - } + return database; + } - if (configFile != null && !configFile.equals("")) { - if (log.isInfoEnabled()) { - log.info("Specified configuration file: '" + configFile + "'"); - } - FileInputStream configXMLFile = new FileInputStream(configFile); + /** + * Get configuration for the named database + */ + protected Configuration getConfiguration(String dbName) throws XMLDBException { + Configuration config = loadConfiguration(); - config = new Configuration(DOMParser.toDocument(configXMLFile), false); - configDir = new File(configFile).getAbsoluteFile().getParent(); - } else { - if (log.isInfoEnabled()) { - log.info("No configuration file specified, going with the default configuration"); + // Find database config with the given database name + Configuration[] roots = config.getChildren("root-collection"); + config = null; + for (int i = 0; i < roots.length; i++) { + if (dbName.equals(roots[i].getAttribute(Database.NAME))) { + config = roots[i]; + break; } - - config = new Configuration(DOMParser.toDocument(Xindice.DEFAULT_CONFIGURATION), false); } - config = config.getChild("root-collection", false); + if (config == null) { + return null; + } + // Figure out database root String dbRoot = config.getAttribute(Database.DBROOT, Database.DBROOT_DEFAULT); if (!new File(dbRoot).isAbsolute()) { - // Let's see if the property was specified. + // Let's see if the XINDICE_DB_HOME property was specified. String home = null; try { // Try configuration first - home = getProperty(Xindice.PROP_XINDICE_DB_HOME); + home = getProperty(PROP_XINDICE_DB_HOME); } catch (XMLDBException ignored) { } if (home == null) { // Fallback to system property - home = System.getProperty(Xindice.PROP_XINDICE_DB_HOME); + home = System.getProperty(SYSPROP_XINDICE_DB_HOME); } if (home != null) { - dbRoot = new File(home + File.separator + dbRoot).getCanonicalPath(); - } else if (configDir != null) { - dbRoot = configDir + File.separator + dbRoot; + try { + dbRoot = new File(home + File.separator + dbRoot).getCanonicalPath(); + } catch (IOException e) { + log.warn("getCanonicalPath failed", e); + dbRoot = new File(home + File.separator + dbRoot).getAbsolutePath(); + } + } else if (configFile != null) { + dbRoot = new File(configFile).getAbsoluteFile().getParent() + File.separator + dbRoot; } else { log.warn("The database configuration file is not specified and there was no " - + Xindice.PROP_XINDICE_DB_HOME + " property set, " + + SYSPROP_XINDICE_DB_HOME + " property set, " + "so Xindice was unable to determine a database location. " + "Database will be created relative to the current directory."); - dbRoot = new File("." + File.separator + dbRoot).getCanonicalPath(); + try { + dbRoot = new File("." + File.separator + dbRoot).getCanonicalPath(); + } catch (IOException e) { + log.warn("getCanonicalPath failed", e); + dbRoot = new File("." + File.separator + dbRoot).getAbsolutePath(); + } + } + try { + config.setAttribute(Database.DBROOT, dbRoot); + } catch (ReadOnlyException ignore) { + } + } + + return config; + } + + private Configuration loadConfiguration() throws XMLDBException { + Configuration config; + + if (configFile == null) { + try { + // Try configuration first + configFile = getProperty(PROP_XINDICE_CONFIGURATION); + } catch (XMLDBException ignored) { + } + if (configFile == null) { + // Fallback to system property + configFile = System.getProperty(SYSPROP_XINDICE_CONFIGURATION); + } + if ("".equals(configFile)) { + configFile = null; + } + } + + if (configFile != null) { + if (log.isInfoEnabled()) { + log.info("Specified configuration file: '" + configFile + "'"); + } + try { + FileInputStream configXMLFile = new FileInputStream(configFile); + config = new Configuration(DOMParser.toDocument(configXMLFile), false); + } catch (XindiceException e) { + throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, + "Unable to parse database configuration file: " + configFile, e); + } catch (FileNotFoundException e) { + throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, + "Database configuration file not found: " + configFile); + } + } else { + if (log.isInfoEnabled()) { + log.info("No configuration file specified, going with the default configuration"); + } + + try { + config = new Configuration(DOMParser.toDocument(Xindice.DEFAULT_CONFIGURATION), false); + } catch (XindiceException e) { + throw new XindiceRuntimeException("Unable to parse default database configuration", e); } - config.setAttribute(Database.DBROOT, dbRoot); } return config; @@ -302,17 +401,12 @@ } } - // TODO: Is this correct behavior? - if (!database.getName().equals(dbName)) { - throw new XMLDBException(ErrorCodes.NO_SUCH_DATABASE, - "Unknown database (must be '" + database.getName() + "'): " + uri); - } - + Database database = getDatabase(dbName, uri); try { return new CollectionImpl(database, colName); } catch (XMLDBException e) { if (e.errorCode == ErrorCodes.NO_SUCH_COLLECTION) { - // per getCollection contract, return null if not found + // Per getCollection contract, return null if not found return null; } throw e;