org.eclipse.smila.search.datadictionary.DataDictionaryController.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smila.search.datadictionary.DataDictionaryController.java

Source

/***********************************************************************************************************************
 * Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying
 * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: brox IT-Solutions GmbH - initial creator
 **********************************************************************************************************************/

package org.eclipse.smila.search.datadictionary;

// data dictionary classes
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DAnyFinderDataDictionary;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DAnyFinderDataDictionaryCodec;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DDException;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DIndex;
import org.eclipse.smila.search.datadictionary.messages.datadictionary.DIndexCodec;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DConfiguration;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DDateField;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DField;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DFieldConfig;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DNumberField;
import org.eclipse.smila.search.datadictionary.messages.ddconfig.DTextField;
import org.eclipse.smila.search.utils.indexstructure.DIndexField;
import org.eclipse.smila.search.utils.indexstructure.DIndexStructure;
import org.eclipse.smila.search.utils.indexstructure.IndexStructureAccess;
import org.eclipse.smila.search.utils.search.IParameter;
import org.eclipse.smila.utils.config.ConfigUtils;
import org.eclipse.smila.utils.workspace.WorkspaceHelper;
import org.eclipse.smila.utils.xml.XMLUtils;
import org.eclipse.smila.utils.xml.XMLUtilsConfig;
import org.eclipse.smila.utils.xml.XMLUtilsException;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * A Class class.
 * <P>
 * 
 * @author BROX IT-Solutions GmbH
 */
public abstract class DataDictionaryController {

    /**
     * The Constant BUNDLE.
     */
    private static final String BUNDLE = "org.eclipse.smila.search.datadictionary";

    /**
     * The Constant CONFIG_NAME.
     */
    private static final String CONFIG_NAME = "DataDictionary.xml";

    /**
     * The _data dictionary types.
     */
    private static DAnyFinderDataDictionary _dataDictionaryTypes;

    /**
     * The dd.
     */
    private static DAnyFinderDataDictionary dd;

    /**
     * The mutex.
     */
    private static Object mutex = new Object();

    /**
     * This method adds an index entry to the data dictionary and saves it.
     * 
     * @param dIndex
     *          the d index
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static void addIndex(final DIndex dIndex) throws DataDictionaryException {
        final Log log = LogFactory.getLog(DataDictionaryController.class);
        synchronized (mutex) {
            ensureLoaded();
            if (hasIndexIgnoreCase(dIndex.getName())) {
                throw new DataDictionaryException(
                        "index already exists in data dictionary [" + getExistingIndexName(dIndex.getName()) + "]");
            }

            final DConfiguration dConfig = dIndex.getConfiguration();
            if (dConfig != null) {
                validateConfiguration(dConfig, dIndex.getIndexStructure());
            }

            dd.addIndex(dIndex);

            // validate data dictionary
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", new ByteArrayOutputStream());
            } catch (final DDException e) {
                log.error("unable to save data dictionary", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary");
            } catch (final XMLUtilsException e) {
                log.error("Unable to stream DataDictionary!", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary while streaming");
            }

            save();
        }
    }

    /**
     * Adds the index.
     * 
     * @param indexTypeName
     *          the index type name
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static void addIndex(final String indexTypeName) throws DataDictionaryException {
        synchronized (dd) {
            ensureLoaded();
            final Enumeration<DIndex> indiceTypes = _dataDictionaryTypes.getIndices();
            while (indiceTypes.hasMoreElements()) {
                final DIndex dIndexType = indiceTypes.nextElement();
                if (dIndexType.getName().equalsIgnoreCase(indexTypeName)) {
                    // CLONE
                    // encode to XML
                    final Document doc = XMLUtils.getDocument();
                    final Element rootElement = doc.createElementNS(DAnyFinderDataDictionaryCodec.NS,
                            "AnyFinderDataDictionary");
                    Attr attr = null;
                    attr = doc.createAttribute("xmlns:xsi");
                    attr.setValue("http://www.w3.org/2001/XMLSchema-instance");
                    rootElement.setAttributeNode(attr);
                    attr = doc.createAttribute("xsi:schemaLocation");
                    attr.setValue(DAnyFinderDataDictionaryCodec.NS + " ../xml/AnyFinderDataDictionary.xsd");
                    rootElement.setAttributeNode(attr);
                    doc.appendChild(rootElement);
                    final DIndex dIndex;
                    try {
                        DIndexCodec.encode(dIndexType, rootElement);
                    } catch (final DDException e) {
                        throw new DataDictionaryException(e);
                    }
                    final Document doc2;
                    try {
                        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", bos);
                        doc2 = XMLUtils.parse(bos.toByteArray(), true);
                    } catch (final XMLUtilsException e) {
                        throw new DataDictionaryException(e);
                    }
                    final DAnyFinderDataDictionary dictionary;
                    // decode again to DIndex
                    try {
                        dictionary = DAnyFinderDataDictionaryCodec.decode(doc2.getDocumentElement());
                    } catch (final DDException e) {
                        throw new DataDictionaryException(e);
                    }
                    dIndex = dictionary.getIndex(dIndexType.getName());
                    addIndex(dIndex);
                    return;
                }
            }
        }
        throw new DataDictionaryException(String.format("Index type [%s] was not found!", indexTypeName));
    }

    /**
     * Rename index.
     * 
     * @param indexName
     *          the index name
     * @param newIndexName
     *          the new index name
     * 
     * @return true, if successful
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static boolean renameIndex(final String indexName, final String newIndexName)
            throws DataDictionaryException {
        final Log log = LogFactory.getLog(DataDictionaryController.class);

        synchronized (mutex) {
            ensureLoaded();
            if (dd.getIndex(newIndexName) != null) {
                throw new DataDictionaryException(
                        String.format("Cannot rename index to [%s] because it's already exists!", newIndexName));
            }
            log.debug("Updating datadictionary...");
            final DIndex index = dd.getIndex(indexName);
            if (index == null) {
                return false; // no index entry
            }
            dd.removeIndex(index);
            index.setName(newIndexName);
            final DIndexStructure indexStructure = index.getIndexStructure();
            if (indexStructure != null) {
                indexStructure.setName(newIndexName);
            }
            dd.addIndex(index);
            // validate data dictionary
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", new ByteArrayOutputStream());
            } catch (final DDException e) {
                log.error("unable to save data dictionary", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary");
            } catch (final XMLUtilsException e) {
                log.error("Unable to stream DataDictionary!", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary while streaming");
            }

            save();
            return true;
        }

    }

    /**
     * This method removes an index entry from the data dictionary. It returnes true, when the index entry has exists.
     * 
     * @param indexName
     *          the index name
     * 
     * @return true, if delete index
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static boolean deleteIndex(final String indexName) throws DataDictionaryException {
        final Log log = LogFactory.getLog(DataDictionaryController.class);

        synchronized (mutex) {
            ensureLoaded();
            log.debug("Updating datadictionary...");
            final DIndex index = dd.getIndex(indexName);
            if (index == null) {
                return false; // no index entry
            }
            dd.removeIndex(index);

            // validate data dictionary
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", new ByteArrayOutputStream());
            } catch (final DDException e) {
                log.error("unable to save data dictionary", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary");
            } catch (final XMLUtilsException e) {
                log.error("Unable to stream DataDictionary!", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary while streaming");
            }

            save();
            return true;
        }
    }

    /**
     * Gets the data dictionary types.
     * 
     * @return the data dictionary types
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static DAnyFinderDataDictionary getDataDictionaryTypes() throws DataDictionaryException {
        ensureLoaded();
        return _dataDictionaryTypes;
    }

    /**
     * Ensure loaded.
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    private static void ensureLoaded() throws DataDictionaryException {
        if (dd == null) {
            synchronized (mutex) {
                if (dd == null) {
                    loadDataDictionary();
                }
            }
        }
    }

    /**
     * Gets the data dictionary.
     * 
     * @return the data dictionary
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static DAnyFinderDataDictionary getDataDictionary() throws DataDictionaryException {
        ensureLoaded();
        return dd;
    }

    /**
     * Checks whether an index with a given name exists and returns that name if such an index is found. If
     * <code>hasIndexIgnoreCase()</code> returns true, this method can be used to obtain the name of the index in the
     * same writing as is used in the data dictionary.
     * 
     * @param indexName -
     *          The name of the index to search for
     * 
     * @return The name of the index as it is stored in the data dictionary
     * 
     * @throws DataDictionaryException
     *           if the data dictionary cannot be loaded
     */
    public static String getExistingIndexName(final String indexName) throws DataDictionaryException {
        ensureLoaded();
        for (final Enumeration e = dd.getIndices(); e.hasMoreElements();) {
            final String name = ((DIndex) e.nextElement()).getName();
            if (name.equalsIgnoreCase(indexName)) {
                return name;
            }
        }
        return null;
    }

    /**
     * Gets the index.
     * 
     * @param indexName
     *          the index name
     * 
     * @return the index
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static DIndex getIndex(final String indexName) throws DataDictionaryException {
        ensureLoaded();
        final DIndex dIndex = dd.getIndex(indexName);
        if (dIndex == null) {
            throw new DataDictionaryException("index does not exist in data dictionary [" + indexName + "]");
        }

        return dIndex;
    }

    /**
     * Checks for index.
     * 
     * @param indexName
     *          the index name
     * 
     * @return true, if successful
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static boolean hasIndex(final String indexName) throws DataDictionaryException {
        ensureLoaded();
        return (dd.getIndex(indexName) != null) ? true : false;
    }

    /**
     * This method checks whether an index with the same name already exists in the data dictionary. The check is
     * performed in a case-insensitive manner.
     * 
     * @param indexName -
     *          The name of the index to check
     * 
     * @return boolean
     * 
     * @throws DataDictionaryException
     *           if the data dictionary cannot be loaded
     */

    public static boolean hasIndexIgnoreCase(final String indexName) throws DataDictionaryException {
        ensureLoaded();
        for (final Enumeration e = dd.getIndices(); e.hasMoreElements();) {
            final String name = ((DIndex) e.nextElement()).getName();
            if (name.equalsIgnoreCase(indexName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Load data dictionary.
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    private static void loadDataDictionary() throws DataDictionaryException {
        synchronized (mutex) {
            final Log log = LogFactory.getLog(DataDictionaryController.class);
            // load data dictionary types from configuration folder
            InputStream is = ConfigUtils.getConfigStream(BUNDLE, CONFIG_NAME);
            _dataDictionaryTypes = parseDataDictionary(is, log);
            // load data dictionary from workspace folder
            File workspace;
            try {
                workspace = WorkspaceHelper.createWorkingDir(BUNDLE);
            } catch (final IOException e) {
                throw new DataDictionaryException(e);
            }
            final File ddFile = new File(workspace, CONFIG_NAME);
            if (!ddFile.exists()) {
                dd = new DAnyFinderDataDictionary();
            } else {
                try {
                    is = new FileInputStream(ddFile);
                } catch (final FileNotFoundException e) {
                    throw new DataDictionaryException(e);
                }
                dd = parseDataDictionary(is, log);
            }
        }
    }

    /**
     * Parses the data dictionary.
     * 
     * @param is
     *          the is
     * @param log
     *          the log
     * 
     * @return the d any finder data dictionary
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    private static DAnyFinderDataDictionary parseDataDictionary(final InputStream is, final Log log)
            throws DataDictionaryException {
        try {
            final Document doc = XMLUtils.parse(is, new XMLUtilsConfig());
            return DAnyFinderDataDictionaryCodec.decode(doc.getDocumentElement());
        } catch (final XMLUtilsException e) {
            log.error("Unable parse DataDictionary!", e);
            throw new DataDictionaryException("Unable parse DataDictionary!");
        } catch (final DDException e) {
            log.error("unable to load data dictionary", e);
            throw new DataDictionaryException("Unable to decode XML into DataDictionary");
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Save.
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    private static void save() throws DataDictionaryException {
        final Log log = LogFactory.getLog(DataDictionaryController.class);

        // resolve datadictionary name
        File workspaceFolder;
        try {
            workspaceFolder = WorkspaceHelper.createWorkingDir(BUNDLE);
        } catch (final IOException e) {
            throw new DataDictionaryException(e);
        }

        // save datadictionary
        try {
            final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
            XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", new File(workspaceFolder, CONFIG_NAME));
        } catch (final DDException e) {
            log.error("unable to save data dictionary", e);
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                log.debug(
                        "invalid data dictionary\n" + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

            } catch (final Throwable ex) {
                ; // do nothing
            }
            throw new DataDictionaryException("unable to update data dictionary");
        } catch (final XMLUtilsException e) {
            log.error("Unable to stream DataDictionary!", e);
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                log.debug(
                        "invalid data dictionary\n" + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

            } catch (final Throwable ex) {
                ; // do nothing
            }
            throw new DataDictionaryException("Unable to stream DataDictionary!");
        }
    }

    /**
     * Sets the index configuration.
     * 
     * @param indexName
     *          the index name
     * @param dConfiguration
     *          the d configuration
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static void setIndexConfiguration(final String indexName, final DConfiguration dConfiguration)
            throws DataDictionaryException {
        final Log log = LogFactory.getLog(DataDictionaryController.class);
        synchronized (mutex) {
            ensureLoaded();

            if (!hasIndex(indexName)) {
                throw new DataDictionaryException("index does not exist in data dictionary [" + indexName + "]");
            }

            // check validity of configuration
            final DIndex dIndex = dd.getIndex(indexName);
            final DIndexStructure dIS = dIndex.getIndexStructure();

            validateConfiguration(dConfiguration, dIS);
            dIndex.setConfiguration(dConfiguration);

            // validate data dictionary
            try {
                final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                XMLUtils.stream(doc.getDocumentElement(), true, "UTF-8", new ByteArrayOutputStream());
            } catch (final DDException e) {
                log.error("unable to save data dictionary", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary");
            } catch (final XMLUtilsException e) {
                log.error("Unable to stream DataDictionary!", e);
                try {
                    final Document doc = DAnyFinderDataDictionaryCodec.encode(dd);
                    log.debug("invalid data dictionary\n"
                            + new String(XMLUtils.stream(doc.getDocumentElement(), false)));

                } catch (final Throwable ex) {
                    ; // do nothing
                }
                throw new DataDictionaryException("invalid data dictionary while streaming");
            }

            save();
        }
    }

    /**
     * Validate configuration.
     * 
     * @param dConfiguration
     *          the d configuration
     * @param dIS
     *          the d is
     * 
     * @throws DataDictionaryException
     *           the data dictionary exception
     */
    public static void validateConfiguration(final DConfiguration dConfiguration, final DIndexStructure dIS)
            throws DataDictionaryException {

        if (dConfiguration == null) {
            throw new DataDictionaryException("No default configuration defined for index");
        }

        // test field count match
        if (dConfiguration.getDefaultConfig().getFieldCount() != dIS.getFieldCount()) {
            throw new DataDictionaryException("Invalid default configuration. Field counts in DefaultConfig "
                    + "and IndexStructure do not match: [" + dConfiguration.getDefaultConfig().getFieldCount() + "/"
                    + dIS.getFieldCount() + "]");
        }

        final IndexStructureAccess indexStructureAccess = IndexStructureAccess.getInstance();
        for (int i = 0; i < dIS.getFieldCount(); i++) {
            final DField field = dConfiguration.getDefaultConfig().getField(i);
            if (field == null) {
                throw new DataDictionaryException("Default configuration missing for field " + i);
            }
            final DFieldConfig configField = field.getFieldConfig();

            final DIndexField dIF = dIS.getField(field.getFieldNo());

            if (dIF != null && !indexStructureAccess.dataTypeMatches(dIF.getType(), configField.getType())) {
                throw new DataDictionaryException("Type of field '" + field.getFieldNo()
                        + "' in DefaultConfig does not match type of index field");
            }

            if (configField.getConstraint() == null) {
                throw new DataDictionaryException(
                        "'Constraint' parameter missing in DefaultConfig for field [" + i + "]");
            }
            if (configField.getWeight() == null) {
                throw new DataDictionaryException(
                        "'Weight' parameter missing in DefaultConfig for field [" + i + "]");
            }

            // check completeness of search technology dependant parameters
            IParameter param = null;
            if (configField instanceof DTextField) {
                param = ((DTextField) configField).getParameter();
            }
            if (configField instanceof DNumberField) {
                param = ((DNumberField) configField).getParameter();
            }
            if (configField instanceof DDateField) {
                param = ((DDateField) configField).getParameter();
            }

            if (param == null) {
                throw new DataDictionaryException(
                        "'Parameter' parameter missing in DefaultConfig for field [" + i + "]");
            }

            if (!param.isComplete()) {
                throw new DataDictionaryException(
                        "'Parameter' parameter is incomplete in DefaultConfig for field [" + i + "]");
            }

        }
    }

}