Package org.apache.xindice.core

Source Code of org.apache.xindice.core.Database

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: Database.java 533273 2007-04-28 00:51:37Z vgritsenko $
*/

package org.apache.xindice.core;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.query.QueryEngine;
import org.apache.xindice.server.Xindice;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.util.ConfigurationException;
import org.apache.xindice.util.Named;
import org.apache.xindice.util.XindiceException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;

/**
* Database is the primary container for the Xindice Database Engine.
*
* @version $Revision: 533273 $, $Date: 2007-04-27 20:51:37 -0400 (Fri, 27 Apr 2007) $
*/
public final class Database extends Collection
                            implements Named {

    private static final Log log = LogFactory.getLog(Database.class);

    // Configuration elements in config/system.xml file
    public static final String DBROOT = "dbroot";
    public static final String NAME = "name";

    private static final String QUERYENGINE = "queryengine";
    private static final String DATABASE = "database";
    private static final String METADATA = "use-metadata";

    public static final String DBROOT_DEFAULT = "./db/";

    // Configuration documents stored in SysConfigs collection
    private static final String COLKEY = "database.xml";
    private static final String METAKEY = "meta.xml";

    private static final Map databases = new HashMap(); // String to Database
    private static final DatabaseShutdownHandler shutdownHandler = new DatabaseShutdownHandler();

    static {
        // sets up our golbal observer. will automatically flush document
        // changes to disk.
        DBObserver.setInstance(new DatabaseChangeObserver());
    }

    /**
     * This will return an instance of a Database for the given
     * name if one has already been loaded, otherwise it will
     * create a new instance.
     *
     * @param config Database configuration
     * @return Database instance
     * @throws ConfigurationException if database name is missing in the configuration
     */
    public static Database getDatabase(Configuration config) {
        String name = config.getAttribute(Database.NAME);

        // No name in the config file ... can't get the database
        if (name == null) {
            throw new ConfigurationException("Database configuration didn't contain a database name");
        }

        Database database = (Database) databases.get(name);
        if (database == null) {
            // In case it's currently being added (only pay the sync hit on a miss)
            synchronized (databases) {
                // Was it created while we waited?
                database = (Database) databases.get(name);
                if (database == null) {
                    database = new Database();
                    try {
                        database.setConfig(config);
                    } catch (XindiceException x) {
                        // TODO: Configurable interface should use ConfigurationException instead of XindiceException... Right?
                        throw new ConfigurationException("XindiceException: " + x.getMessage(), x);
                    }

                    databases.put(database.getName(), database);
                }
            }
        }

        return database;
    }

    /**
     * This will merely return an instance of a Database for the given
     * name if one has already been loaded.
     *
     * @param name Database name
     * @return Database
     */
    public static Database getDatabase(String name) {
        Database database = (Database) databases.get(name);
        if (database == null) {
            // in case it's currently being added (only pay the sync hit on a miss)
            synchronized (databases) {
                database = (Database) databases.get(name);
            }
        }

        return database;
    }

    public static String[] listDatabases() {
        synchronized (databases) {
            return (String[]) databases.keySet().toArray(new String[0]);
        }
    }


    //
    // Instance...
    //

    private DocumentCache docCache;
    private QueryEngine engine;
    private boolean metaEnabled;
    private boolean metaInit;
    private MetaSystemCollection metaSystemCollection;
    private boolean sysInit;
    private SystemCollection systemCollection;
    private FileOutputStream lock;
    private boolean closed;

    /** Shared timer instance for this database's IndexManagers */
    private Timer timer;


    public Database() {
        super();
        docCache = new DocumentCache();
        engine = new QueryEngine(this);
        closed = true;
    }

    /**
     * Checks to see if it has been closed to insure that it doesn't try to do it
     * twice.
     *
     * @param removeFromShutdown If true removes its self from the shutdown hook
     * @return true if closed
     * @throws DBException if unable to close
     */
    protected synchronized boolean close(boolean removeFromShutdown) throws DBException {
        if (removeFromShutdown) {
            // we have already been closed so no need to do this again.
            shutdownHandler.removeDatabase(this);
        }

        // check to see if we have already been closed.
        if (!closed) {
            if(log.isDebugEnabled()) {
                log.debug("Shutting down database: '" + getName() + "'");
            }

            flushConfig();
            super.close();

            // Release database lock
            try {
                this.lock.close();
                new File(getCollectionRoot(), "db.lock").delete();
            } catch (Exception e) {
                // Ignore IO exception
            }
            this.lock = null;

            synchronized (databases) {
                databases.remove(getName());
            }

            // Stop the timer thread
            timer.cancel();

            closed = true;
        }

        return true;
    }

    /**
     * @see org.apache.xindice.core.DBObject#close()
     * @see org.apache.xindice.core.Database#close(boolean removeFromShutdown)
     */
    public boolean close() throws DBException {
        return close(true);
    }

    /**
     * flushConfig ensures that the Collection configuration has been
     * properly flushed to disk after a modification.
     */
    public void flushConfig() {
        if (getConfig().isDirty()) {
            try {
                Document d = getConfig().getElement().getOwnerDocument();
                systemCollection.getCollection(SystemCollection.CONFIGS).setDocument(COLKEY, d);
                getConfig().resetDirty();
            } catch (Exception e) {
                log.error("Error Writing Configuration '" + COLKEY + "', for database " + getName(), e);
            }

            // Observer
            DBObserver.getInstance().flushDatabaseConfig(this, getConfig());
        }

        if (isMetaEnabled()) {
            Configuration config = metaSystemCollection.getConfig();
            if (config.isDirty()) {
                try {
                    Document d = config.getElement().getOwnerDocument();
                    systemCollection.getCollection(SystemCollection.CONFIGS).setDocument(METAKEY, d);
                    config.resetDirty();
                } catch (Exception e) {
                    log.error("Error writing configuration '" + METAKEY + "', for database " + getName(), e);
                }
            }
        }
    }

    /**
     * @see org.apache.xindice.core.Collection#getDatabase()
     */
    public Database getDatabase() {
        return this;
    }

    /**
     * getDocumentCache returns the Database-level Document Cache.
     *
     * @return The DocumentCache
     */
    public DocumentCache getDocumentCache() {
        return docCache;
    }

    /**
     * Return the MetaSystem collection for this database.
     *
     * It will return null if metadata is not enabled on this database.
     * @return MetaSystemCollection
     */
    public MetaSystemCollection getMetaSystemCollection() {
        return metaSystemCollection;
    }

    /**
     * getQueryEngine returns a reference to the Database's current
     * operating QueryEngine implementation.
     *
     * @return The QueryEngine instance
     */
    public QueryEngine getQueryEngine() {
        return engine;
    }

    /**
     * @see org.apache.xindice.core.Collection#getSystemCollection()
     */
    public SystemCollection getSystemCollection() {
        return systemCollection;
    }

    /**
     * Return database's timer instance.
     * @return Database timer instance
     */
    protected Timer getTimer() {
        return timer;
    }

    /**
     * Return whether or not metadata is enabled on this database.
     * @return boolean
     */
    public boolean isMetaEnabled() {
        return metaEnabled;
    }

    /**
     * @see org.apache.xindice.util.Configurable#setConfig(org.apache.xindice.util.Configuration)
     */
    public void setConfig(Configuration config) throws XindiceException {
        // FIXME Get rid of super.setConfig here?
        super.setConfig(config);
        setCanonicalName('/' + getName());

        // Determine database root directory
        String dbroot = config.getAttribute(DBROOT);
        File dbrootDir = new File(dbroot);
        if (!dbrootDir.isAbsolute()) {
            // Here, DB root already should be absolute. XMLRPC, Embed, and Managed drivers take care of it.
            log.warn("The specified database root directory '" + dbroot + "' is relative. " +
                     "Using property " + Xindice.PROP_XINDICE_DB_HOME + " to resolve.");

            String home = System.getProperty(Xindice.PROP_XINDICE_DB_HOME);
            if (home == null) {
                log.warn("The specified database root directory '" + dbroot + "' is relative " +
                         "and there was no " + Xindice.PROP_XINDICE_DB_HOME + " property set, " +
                         "so Xindice was unable to determine a database location. " +
                         "Database will be created relative to the current working directory.");

                home = ".";
            }
            try {
                // In case home has been specified as relative path convert it to absolute path
                dbrootDir = new File(home, dbroot).getCanonicalFile();
            } catch (IOException e) {
                throw new XindiceException("Can't get canonical path", e);
            }
        }
        setCollectionRoot(dbrootDir);
        if (log.isInfoEnabled()) {
            log.info("Database points to " + dbrootDir.getAbsolutePath());
        }

        // Put a lock (at least attempt to) on the database
        // FIXME: Use JDK1.4 FileLock
        File lock = new File(getCollectionRoot(), "db.lock");
        try {
            if (lock.exists() && !lock.delete()) {
                throw new IOException("Could not delete lock file.");
            }
            this.lock = new FileOutputStream(lock);
            this.lock.write(new Date().toString().getBytes());
        } catch (IOException e) {
            throw new XindiceException("Unable to open lock file " + lock + ". " +
                                       "Make sure database is not open by another process. " +
                                       "Exception: " + e);
        }

        // Now we are ready to open it up
        shutdownHandler.registerDatabase(this);
        timer = new Timer(false);
        closed = false;

        // Initialize query engine
        try {
            Configuration queryCfg = config.getChild(QUERYENGINE);
            if (queryCfg != null) {
                engine.setConfig(queryCfg);
            }
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }

        // Initialize system collection
        if (!sysInit) {
            systemCollection = new SystemCollection(this);
            systemCollection.init();
            super.addCollection(systemCollection);
            this.sysInit = true;
        }

        Collection sysConfigCollection = systemCollection.getCollection(SystemCollection.CONFIGS);
        try {
            // Bootstrap from the database itself...  This is accomplished
            // by intercepting the setConfig call and using a Configuration
            // retrieved from the database instead of the standard config
            Document colDoc = sysConfigCollection.getDocument(COLKEY);
            if (colDoc == null) {
                DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                colDoc = db.newDocument();
                Element root = colDoc.createElement(DATABASE);
                root.setAttribute(NAME, getName());
                colDoc.appendChild(root);
                sysConfigCollection.setDocument(COLKEY, colDoc);
            }

            super.setConfig(new Configuration(colDoc.getDocumentElement(), false));
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }

        // Register the Database with the VM
        // databases.put(getName(), this);

        // initialize the meta collection
        // but only if it's turned on in the config.
        String metaCfg = config.getAttribute(METADATA);
        if (metaCfg.equalsIgnoreCase("on")) {
            metaEnabled = true;
        }

        if (metaEnabled && !metaInit) {
            try {
                metaSystemCollection = new MetaSystemCollection(this);

                Document colDoc = sysConfigCollection.getDocument(METAKEY);
                if (colDoc == null) {
                    metaSystemCollection.init();
                    Document metaConfig = metaSystemCollection.getConfig().getElement().getOwnerDocument();
                    sysConfigCollection.setDocument(METAKEY, metaConfig);
                } else {
                    metaSystemCollection.setConfig(new Configuration(colDoc, false));
                }

                super.addCollection(metaSystemCollection);
                metaInit = true;
                if (log.isDebugEnabled()) {
                    log.debug("Meta collection is initialized");
                }
            } catch (Exception e) {
                log.error("Meta collection was not initialized", e);
            }
        }

        // observer
        DBObserver.getInstance().setDatabaseConfig(this, getCollections(), config);
    }
}
TOP

Related Classes of org.apache.xindice.core.Database

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.