blob: 5aa95865b15be4d0be810610a7d31df5e2045fdb [file] [log] [blame]
/***********************************************************************************************************************
* 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
+ "]");
}
}
}
}