Package org.apache.xindice.core.indexer

Source Code of org.apache.xindice.core.indexer.IndexManager$PopulateIndexersTimerTask

/*
* 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: IndexManager.java 571354 2007-08-31 01:49:58Z natalia $
*/

package org.apache.xindice.core.indexer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.Stopwatch;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.DBException;
import org.apache.xindice.core.data.Entry;
import org.apache.xindice.core.data.Key;
import org.apache.xindice.core.data.RecordSet;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.util.ConfigurationCallback;
import org.apache.xindice.util.SimpleConfigurable;
import org.apache.xindice.util.XindiceException;
import org.apache.xindice.xml.SymbolTable;

import org.w3c.dom.Document;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.WeakHashMap;

/**
* IndexManager is a class that manages Indexes.  Good description, eh?
* I should win a Pulitzer Prize for that one.
*
* @version $Revision: 571354 $, $Date: 2007-08-30 21:49:58 -0400 (Thu, 30 Aug 2007) $
*/
public final class IndexManager extends SimpleConfigurable {

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

    private static final String[] EMPTY_STRINGS = new String[0];
    private static final Indexer[] EMPTY_INDEXERS = new Indexer[0];

    private static final String INDEX = "index";
    private static final String NAME  = "name";
    private static final String CLASS = "class";

    private static final Integer STATUS_READY = new Integer(0);
    private static final Integer STATUS_BUSY = new Integer(1);

    private Map indexes = new HashMap();      // Name to Indexer
    private Map status = new HashMap();       // Name to Status
    private Map bestIndexers = new HashMap(); // String to Map of IndexPattern to Indexer

    private Collection collection;
    private Timer timer;
    private SymbolTable symbols;
    private final List newIndexers = new ArrayList(); // of Indexers

    private int taskCount;                      // counter of scheduled tasks
    private final Object lock = new Object();   // lock object for manipulating taskCounter

    /**
     * Create IndexManager for a given collection
     *
     * @param collection Collection for this IndexManager
     * @param timer Timer for indexing tasks
     */
    public IndexManager(Collection collection, Timer timer) {
        this.collection = collection;
        this.symbols = collection.getSymbols();
        this.timer = timer;
    }

    /**
     * Configure index manager, register all indexes specified in the configuration
     *
     * @param config IndexManager configuration
     */
    public void setConfig(Configuration config) throws XindiceException {
        super.setConfig(config);

        config.processChildren(INDEX, new ConfigurationCallback() {
            public void process(Configuration cfg) {
                String className = cfg.getAttribute(CLASS);
                try {
                    register(Class.forName(className), cfg);
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Failed to register index with class '" + className + "' for collection '" + collection.getCanonicalName() + "'", e);
                    }
                }
            }
        });
    }

    /**
     * list returns a list of the Indexers that this IndexerManager has
     * registered.
     *
     * @return An array containing the Indexer names
     */
    public synchronized String[] list() {
        return (String[]) indexes.keySet().toArray(EMPTY_STRINGS);
    }

    /**
     * drop physically removes the specified Indexer and any
     * associated system resources that the Indexer uses.
     *
     * @param name The Indexer to drop
     * @return Whether or not the Indexer was dropped
     */
    public synchronized boolean drop(final String name) {
        // Get indexer
        Indexer idx = get(name);

        // Unregister and remove from coniguration
        unregister(name);
        config.processChildren(INDEX, new ConfigurationCallback() {
            public void process(Configuration cfg) {
                try {
                    if (cfg.getAttribute(NAME).equals(name)) {
                        cfg.delete();
                    }
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("ignored exception", e);
                    }
                }
            }
        });

        // Drop indexer
        boolean res = false;
        try {
            res = idx.drop();
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }
        return res;
    }

    /**
     * Drop all indexers
     */
    public synchronized void drop() {
        String[] names = (String[]) indexes.keySet().toArray(new String[0]);
       
        // Drop indexes
        for (int i = 0; i < names.length; i++) {
            drop(names[i]);
        }
    }

    /**
     * create creates a new Indexer object and any associated
     * system resources that the Indexer will need.
     *
     * @param cfg The Indexer's configuration
     * @return The Indexer that was created
     * @throws DBException if unable to create specified indexer
     */
    public synchronized Indexer create(Configuration cfg) throws DBException {
        if (!INDEX.equals(cfg.getName())) {
            throw new CannotCreateException("Cannot create index in " + collection.getCanonicalName() +
                                            ". Index configuration top element must be 'index'");
        }
       
        String name = cfg.getAttribute(NAME);
        try {
            // Check for duplicates
            Configuration[] cfgs = config.getChildren();
            for (int i = 0; i < cfgs.length; i++) {
                if (cfgs[i].getAttribute(NAME).equals(name)) {
                    throw new DuplicateIndexException("Duplicate Index '" + name + "' in collection '" + collection.getCanonicalName() + "'");
                }
            }

            String className = cfg.getAttribute(CLASS);
            Indexer idx = register(Class.forName(className), cfg);
            config.add(cfg);

            return idx;
        } catch (DBException e) {
            throw e;
        } catch (Exception e) {
            throw new CannotCreateException("Cannot create index '" + name + "' in " + collection.getCanonicalName(), e);
        }
    }

    /**
     * Closes all indexers managed by this index manager.
     */
    public synchronized void close() {
        // wait for all scheduled tasks to finish
        synchronized (lock) {
            while (taskCount > 0) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        }

        // close all indexers
        for (Iterator i = indexes.values().iterator(); i.hasNext(); ) {
            Indexer idx = (Indexer) i.next();
            try {
                idx.close();
            } catch (DBException e) {
                if (log.isWarnEnabled()) {
                    log.warn("Failed to close indexer " + idx.getName() + " on collection " + collection.getCanonicalName(), e);
                }
            }
        }
    }

    public synchronized Indexer register(Class c, Configuration cfg) throws DBException {
        String name = null;
        try {
            Indexer idx = (Indexer) c.newInstance();
            initialize(idx, cfg);

            name = idx.getName();
            if (name == null || name.trim().equals("")) {
                throw new CannotCreateException("No name specified");
            }

            if (!idx.exists()) {
                idx.create();
                idx.open();

                status.put(name, STATUS_BUSY);
                synchronized (newIndexers) {
                    newIndexers.add(idx);
                }

                synchronized (lock) {
                    taskCount++;
                    try {
                        // Schedule new task
                        timer.schedule(new PopulateIndexersTimerTask(), 0);
                    } catch (RuntimeException e) {
                        // If failed to schedule the task, decrease the counter.
                        taskCount--;
                        throw e;
                    } catch (Error e) {
                        // If failed to schedule the task, decrease the counter.
                        taskCount--;
                        throw e;
                    }

                    if (log.isDebugEnabled()) {
                        log.debug("Scheduled new task, count is " + taskCount);
                    }
                }
            } else {
                status.put(name, STATUS_READY);
                idx.open();
            }
            indexes.put(name, idx);

            Map tbl = (Map) bestIndexers.get(idx.getIndexStyle());
            if (tbl != null) {
                tbl.clear();
            }

            return idx;
        } catch (DBException e) {
            throw e;
        } catch (Exception e) {
            throw new CannotCreateException("Cannot create Index '" + name + "' in " + collection.getCanonicalName(), e);
        }
    }

    public synchronized void unregister(String name) {
        Indexer idx = (Indexer) indexes.remove(name);
        status.remove(name);

        String style = idx.getIndexStyle();
        Map tbl = (Map) bestIndexers.get(style);
        if (tbl != null) {
            tbl.clear();
        }
    }

    private void initialize(Indexer idx, Configuration cfg) throws XindiceException {
        idx.setCollection(collection);
        idx.setConfig(cfg);
    }

    private void populateNewIndexers() throws DBException {
        Indexer[] list;
        synchronized (newIndexers) {
            list = (Indexer[]) newIndexers.toArray(EMPTY_INDEXERS);
            newIndexers.clear();
        }

        if (list.length > 0) {
            if (log.isTraceEnabled()) {
                for (int i = 0; i < list.length; i++) {
                    log.trace("Index Creation: " + list[i].getName());
                }
            }

            Stopwatch sw = new Stopwatch("Populated Indexes", true);
            RecordSet rs = collection.getFiler().getRecordSet();
            while (rs.hasMoreRecords()) {
                // Read only key, we don't need filer-level value
                Key key = rs.getNextKey();
                Entry entry = collection.getEntry(key);
                if (entry.getEntryType() == Entry.DOCUMENT) {
                    try {
                        new DocumentHandler(symbols, key, (Document) entry.getValue(), DocumentHandler.ACTION_CREATE, list);
                    } catch (Exception e) {
                        if (log.isWarnEnabled()) {
                            log.warn("Failed to index document " + key, e);
                        }
                    }
                }
            }

            for (int i = 0; i < list.length; i++) {
                try {
                    list[i].flush();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("ignored exception", e);
                    }
                }
                status.put(list[i].getName(), STATUS_READY);
            }
            sw.stop();

            if (log.isDebugEnabled()) {
                for (int i = 0; i < list.length; i++) {
                    log.debug("Index Complete: " + list[i].getName());
                }
                log.debug(sw.toString());
            }
        }
    }

    /**
     * get retrieves an Indexer by name.
     *
     * @param name The Indexer name
     * @return The Indexer
     */
    public synchronized Indexer get(String name) {
        return (Indexer) indexes.get(name);
    }

    /**
     * getBestIndexer retrieves the best Indexer to use for the specified
     * IndexPattern.
     *
     * @param style The Indexer Style (ex: Node, Value)
     * @param pattern The IndexPattern to use
     * @return The best Indexer (or null)
     */
    public Indexer getBestIndexer(String style, IndexPattern pattern) {
        Map tbl = (Map) bestIndexers.get(style);
        if (tbl == null) {
            tbl = new WeakHashMap(); // FIXME: Review usage of WeakHashMap
            bestIndexers.put(style, tbl);
        }

        Indexer idx = (Indexer) tbl.get(pattern);
        if (idx == null) {
            int highScore = 0;
            Iterator i = indexes.values().iterator();
            while (i.hasNext()) {
                Indexer index = (Indexer) i.next();
                // Indexer is not ready: can not use it
                if (!status.get(index.getName()).equals(STATUS_READY)) {
                    continue;
                }

                // Indexer is of different style: can not use it
                if (!index.getIndexStyle().equals(style)) {
                    continue;
                }

                // TODO: should it check patterns?
                // there can be only one full text index for a collection
                if (style.equals(Indexer.STYLE_FULLTEXT)) {
                    return index;
                }

                for (int j = 0; j < index.getPatterns().length; j++) {
                    int score = pattern.getMatchLevel(index.getPatterns()[j]);

                    if (score > highScore) {
                        idx = index;
                        highScore = score;
                    }
                }

            }
            tbl.put(pattern, idx);
        }
        return idx;
    }

    public void addDocument(Key key, Document doc) {
        if (indexes.size() > 0) {
            Indexer[] idxList = (Indexer[]) indexes.values().toArray(EMPTY_INDEXERS);
            new DocumentHandler(symbols, key, doc, DocumentHandler.ACTION_CREATE, idxList);
            for (int i = 0; i < idxList.length; i++) {
                try {
                    idxList[i].flush();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("ignored exception", e);
                    }
                }
            }
        }
    }

    public void removeDocument(Key key, Document doc) {
        if (indexes.size() > 0) {
            Indexer[] idxList = (Indexer[]) indexes.values().toArray(EMPTY_INDEXERS);
            new DocumentHandler(symbols, key, doc, DocumentHandler.ACTION_DELETE, idxList);
            for (int i = 0; i < idxList.length; i++) {
                try {
                    idxList[i].flush();
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("ignored exception", e);
                    }
                }
            }
        }
    }

    private class PopulateIndexersTimerTask extends TimerTask {
        public void run() {
            try {
                populateNewIndexers();
            } catch (DBException e) {
                if (log.isWarnEnabled()) {
                    log.warn("ignored exception", e);
                }
            } finally {
                synchronized (lock) {
                    taskCount--;
                    if (log.isDebugEnabled()) {
                        log.debug("Task completed, count is " + taskCount);
                    }
                    if (taskCount == 0) {
                        lock.notifyAll();
                    }
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.xindice.core.indexer.IndexManager$PopulateIndexersTimerTask

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.