Java tutorial
/*********************************************************************************************************************** * 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 + "]"); } } } }