All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.julielab.xmlData.config.ConfigReader Maven / Gradle / Ivy

Go to download

A utility for managing documents stored in a PostgreSQL database. The documents are imported into a PostgreSQL DB as full texts with the goal to be able to retrieve the documents by their PubMedID efficiently. For more sophisticated tasks, a user configuration file can be delivered which can take control of the table schema to use, the PostgreSQL schema to use and the actual database server to connect to as well as the concrete database.

There is a newer version: 1.6.2
Show newest version
/**
 * ConfigurationParser.java
 *
 * Copyright (c) 2011, JULIE Lab.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 *
 * Author: faessler/hellrich
 *
 * Current version: 1.0
 * Since version:   1.0
 *
 * Creation date: 22.03.2011
 **/

package de.julielab.xmlData.config;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ximpleware.AutoPilot;
import com.ximpleware.NavException;
import com.ximpleware.VTDException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XMLModifier;
import com.ximpleware.XPathEvalException;
import com.ximpleware.XPathParseException;

import de.julielab.xml.JulieXMLTools;

/**
 * This class reads an xml configuration file, containing the definition of a
 * database connection and the fields used in the database. It provides those
 * definitions as specialized objects.
 * 
 * @author hellrich
 */
public class ConfigReader {

	private static final Logger LOG = LoggerFactory
			.getLogger(ConfigReader.class);
	private final static int BUFFER_SIZE = 1000;
	public static final String DEFAULT_DEFINITION = "/defaultConfiguration.xml";

	public static final String XPATH_ACTIVE_TABLE_SCHEMA = "//activeTableSchema";
	public static final String XPATH_ACTIVE_DB = "//activeDBConnection";
	public static final String XPATH_ACTIVE_PG_SCHEMA = "//activePostgresSchema";
	public static final String XPATH_MAX_CONNS = "//maxActiveDBConnections";

	public static final String XPATH_CONF_DBS = "//DBConnections";
	public static final String XPATH_CONF_SCHEMAS = "//tableSchemas";

	public static final String XPATH_CONF_DB = "//DBConnection";
	public static final String XPATH_CONF_SCHEMA = "//tableSchema";

	private static final int INDEX_SCHEMA = 0;
	private static final int INDEX_DB = 1;
	private static final int INDEX_PG_SCHEMA = 2;
	private static final int INDEX_MAX_CONNS = 3;
	private static final int INDEX_DATA_TABLE = 4;
	private static final int INDEX_DATA_SCHEMA = 5;

	private static final String XPATH_DATA_TABLE = "//dataTable";
	private static final String XPATH_DATA_SCHEMA = "//activeDataPostgresSchema";
	private static final String ATTRIBUTE_NAME = "name";

	private FieldConfigurationManager fieldConfigs;
	private DBConfig dbConf;
	private String activeDataTable;
	private String activeSchemaName;
	private byte[] mergedConfigData;
	private String activeDataSchema;
	private List schemaNames;

	public ConfigReader(InputStream def) {
		try {
			byte[] defaultConfData = null;
			byte[] userConfData = null;

			InputStream is = getClass().getResourceAsStream(DEFAULT_DEFINITION);
			defaultConfData = IOUtils.toByteArray(is);
			is.close();
			// check if the user gave a table schema definition;
			// if not, the default values will be used
			if (def != null) {
				userConfData = IOUtils.toByteArray(def);
				def.close();
			}
			mergedConfigData = mergeConfigData(defaultConfData, userConfData);
			
			schemaNames = getAllSchemaNames(mergedConfigData);

			// Creating
			fieldConfigs = new FieldConfigurationManager();
			for (String schemaName : schemaNames)
				fieldConfigs.put(schemaName, new FieldConfig(mergedConfigData,
						schemaName));
			dbConf = new DBConfig(mergedConfigData);
			activeDataTable = ConfigBase.getActiveConfig(mergedConfigData,
					XPATH_DATA_TABLE);
			activeDataSchema = ConfigBase.getActiveConfig(mergedConfigData,
					XPATH_DATA_SCHEMA);
			activeSchemaName = ConfigBase.getActiveConfig(mergedConfigData,
					XPATH_ACTIVE_TABLE_SCHEMA);
			
			LOG.debug("Active data table: {}", activeDataTable);
			LOG.debug("Active Postgres data schema: {}", activeDataSchema);
			LOG.debug("Active table schema: {}", activeSchemaName);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (VTDException e) {
			LOG.error("Parsing of configuration file failed:", e);
		}

	}

	/**
	 * @param mergedConfigData
	 * @return
	 */
	private List getAllSchemaNames(byte[] mergedConfigData)
			throws VTDException {
		List schemaNames = new ArrayList();

		VTDGen vg = new VTDGen();
		vg.setDoc(mergedConfigData);
		vg.parse(true);
		VTDNav vn = vg.getNav();
		// Navigates through schema elements
		AutoPilot schemaAP = new AutoPilot(vn);
		schemaAP.selectXPath(XPATH_CONF_SCHEMA);
		// Returns the name attribute value for current schema, navigated to by
		// schemaAP.
		AutoPilot schemaNameAP = new AutoPilot(vn);
		schemaNameAP.selectXPath("@name");

		while (schemaAP.evalXPath() != -1)
			schemaNames.add(schemaNameAP.evalXPathToString());

		return schemaNames;
	}

	/**
	 * Inserts the user schemes into the default configuration. This makes all
	 * data available in one place, which is useful for referencing default
	 * values from within a user configuration.
	 * 
	 * @param defaultConfData
	 *            - prepared default configuration file
	 * @param userConfData
	 *            - prepared user specific configuration file
	 * @return - the merged configuration
	 * @throws VTDException
	 * @throws IOException
	 */
	protected static byte[] mergeConfigData(byte[] defaultConfData,
			byte[] userConfData) throws VTDException, IOException {
		VTDGen vg = new VTDGen();
		vg.setDoc(defaultConfData);
		vg.parse(true);
		VTDNav vn = vg.getNav();
		AutoPilot ap = new AutoPilot(vn);

		if (userConfData == null) {
		    return defaultConfData;
            //throw new IllegalArgumentException("No CoStoSys user configuration was passed.");
        }

		XMLModifier xm = new XMLModifier(vn);

		// Get user defined schema and DB connection data.
		byte[][] userDefs = extractConfigData(userConfData);

		// Add schema data to the default configuration.
		if (userDefs[INDEX_SCHEMA] != null) {
			ap.selectXPath(XPATH_CONF_SCHEMA);
			if (ap.evalXPath() != -1) {
				xm.insertAfterElement(userDefs[INDEX_SCHEMA]);
			}
		}

		// Add DB connection data to the default configuration.
		if (userDefs[INDEX_DB] != null) {
			ap.selectXPath(XPATH_CONF_DB);
			if (ap.evalXPath() != -1) {
				xm.insertAfterElement(userDefs[INDEX_DB]);
			}
		}

		// Get active table schema, active data postgres schema, active DB
		// connection and more
		// from the user configuration, if these declarations exist.
		String[] activeConfs = getActiveConfigurations(userConfData);
		LOG.debug("Found the following active configurations in the user data: {}", Arrays.toString(activeConfs));

		// Insert the active configurations into the merged configuration, thus
		// overwriting the defaults.
		if (activeConfs[INDEX_SCHEMA].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_ACTIVE_TABLE_SCHEMA, activeConfs[INDEX_SCHEMA]);
			LOG.trace("Set the active table schema to {}. Returned new index: {}", activeConfs[INDEX_SCHEMA], newTextIndex);
			if (newTextIndex == -1) {
				throw new IllegalStateException(
						"There is no active table schema defined. Please define an active table schema in your user " +
                                " configuration. The user configuration is: " + new String(userConfData, StandardCharsets.UTF_8));
			}
		}
		if (activeConfs[INDEX_DB].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_ACTIVE_DB, activeConfs[INDEX_DB]);
			if (newTextIndex == -1) {
//				throw new IllegalStateException(
//						"Unexpected error: The default configuration does not define an active database connection. Please define an active DB connection in your user configuration.");
				LOG.warn("The default configuration does not define an active database connection.");
			}
			
		}
		if (activeConfs[INDEX_PG_SCHEMA].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_ACTIVE_PG_SCHEMA, activeConfs[INDEX_PG_SCHEMA]);
			if (newTextIndex == -1)
				throw new IllegalStateException(
						"Unexpected error: The default configuration does not define an active Postgres schema. Please define an active Postgres schema in your user configuration.");
		}
		if (activeConfs[INDEX_MAX_CONNS].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_MAX_CONNS, activeConfs[INDEX_MAX_CONNS]);
			if (newTextIndex == -1) {
//				throw new IllegalStateException(
//						"Unexpected error: The default configuration does not define a maximal number of database connections. Please define a maximal number of connections in your user configuration.");
				LOG.warn("Unexpected error: The default configuration does not define a maximal number of database connections");
			}
		}
		if (activeConfs[INDEX_DATA_TABLE].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_DATA_TABLE, activeConfs[INDEX_DATA_TABLE]);
			if (newTextIndex == -1)
				throw new IllegalStateException(
						"Unexpected error: The default configuration does not define a _data table. Please define a _data table in your user configuration.");
		}
		
		if (activeConfs[INDEX_DATA_SCHEMA].length() > 0) {
			int newTextIndex = JulieXMLTools.setElementText(vn, ap, xm,
					XPATH_DATA_SCHEMA, activeConfs[INDEX_DATA_SCHEMA]);
			if (newTextIndex == -1)
				throw new IllegalStateException(
						"Unexpected error: The default configuration does not define an active data Postgres schema. Please define a data postgres schema in your user configuration.");
		}

		// Test validity of merged xml (no doublets)
		vn = xm.outputAndReparse();

		String doublet = getDoublet(vn, XPATH_CONF_SCHEMA);
		if (doublet != null)
			throw new IllegalStateException(
					"Unexpected error: You may not define "
							+ doublet
							+ "as this schema is already defined in the default config!");

		doublet = getDoublet(vn, XPATH_CONF_DB);
		if (doublet != null)
			throw new IllegalStateException(
					"Unexpected error: You may not define "
							+ doublet
							+ "as this connection is already defined in the default config!");

		// Return the merged configuration data.
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		xm.output(os);
		return os.toByteArray();
	}

	private static String getDoublet(VTDNav vn, String xpath) {
		String doublet = "";
		AutoPilot ap = new AutoPilot(vn);
		try {
			ap.selectXPath(xpath);
			int index = ap.evalXPath();
			String name = null;
			Set found = new HashSet();
			while (index != -1) {
				int attrIndex = vn.getAttrVal(ATTRIBUTE_NAME);
				if (attrIndex != -1) {
					name = vn.toString(attrIndex);
					if (found.contains(name))
						doublet = doublet.concat(name).concat(", ");
					else
						found.add(name);
				}
				index = ap.evalXPath();
			}
		} catch (XPathParseException e) {
			e.printStackTrace();
		} catch (XPathEvalException e) {
			e.printStackTrace();
		} catch (NavException e) {
			e.printStackTrace();
		}
		return doublet.equals("") ? null : doublet;
	}

	/**
	 * Extracts the active configuration names (e.g. table schema) from the
	 * configuration data given by confData. Returns an array of
	 * active configuration names where the position of a specific active
	 * configuration name in the array is determined by the constants
	 * INDEX_SCHEMA, INDEX_DB etc.
	 * 
	 * @param confData
	 *            Configuration data to extract active configuration names from.
	 * @return A String array with the names of active configurations. The Array
	 *         is index by the INDEX_XXX constants.
	 * @throws VTDException
	 *             If something concerning the parsing and value extraction goes
	 *             wrong.
	 */
	private static String[] getActiveConfigurations(byte[] confData)
			throws VTDException {
		VTDGen vg = new VTDGen();
		vg.setDoc(confData);
		vg.parse(true);
		VTDNav vn = vg.getNav();
		AutoPilot ap = new AutoPilot(vn);

		String[] activeConfigurations = new String[6];
		ap.selectXPath(XPATH_ACTIVE_PG_SCHEMA);
		activeConfigurations[INDEX_PG_SCHEMA] = ap.evalXPathToString();

		ap.selectXPath(XPATH_ACTIVE_TABLE_SCHEMA);
		activeConfigurations[INDEX_SCHEMA] = ap.evalXPathToString();

		ap.selectXPath(XPATH_ACTIVE_DB);
		activeConfigurations[INDEX_DB] = ap.evalXPathToString();


		ap.selectXPath(XPATH_MAX_CONNS);
		activeConfigurations[INDEX_MAX_CONNS] = ap.evalXPathToString();
		
		ap.selectXPath(XPATH_DATA_TABLE);
		activeConfigurations[INDEX_DATA_TABLE] = ap.evalXPathToString();

		ap.selectXPath(XPATH_DATA_SCHEMA);
		activeConfigurations[INDEX_DATA_SCHEMA] = ap.evalXPathToString();

		return activeConfigurations;
	}

	/**
	 * Retrieves XML elements (determined by the used path) from the
	 * configuration.
	 * 
	 * @param confData
	 *            - prepared XML
	 * @return - the retrieved element
	 * @throws IOException
	 * @throws VTDException
	 */
	protected static byte[][] extractConfigData(byte[] confData)
			throws IOException, VTDException {
		// Allocate space for schema and DB connection data.
		byte[][] configData = new byte[2][];
		VTDGen vg = new VTDGen();
		vg.setDoc(confData);
		vg.parse(true);
		VTDNav vn = vg.getNav();
		AutoPilot ap = new AutoPilot(vn);

		// Get schema definition data.
		ap.selectXPath(XPATH_CONF_SCHEMAS);

		if (ap.evalXPath() != -1) {
			String fragment = JulieXMLTools.getFragment(vn, JulieXMLTools.CONTENT_FRAGMENT,
					true);
			configData[INDEX_SCHEMA] = fragment.getBytes();
		}

		// Get database connection data.
		ap.selectXPath(XPATH_CONF_DBS);

		if (ap.evalXPath() != -1) {
			String fragment = JulieXMLTools.getFragment(vn, JulieXMLTools.CONTENT_FRAGMENT,
					true);
			configData[INDEX_DB] = fragment.getBytes();
		}

		return configData;
	}

	/**
	 * Accessing the Database Configuration
	 * 
	 * @return - DatabaseConfig Object
	 */
	public DBConfig getDatabaseConfig() {
		return dbConf;
	}

	/**
	 * 

* Accessing the Field Definitions. *

*

* The returned map consists of pairs in the form * (schemaName, fieldConfig) where schemaName is * the name of the table schema represented by fieldConfig. *

* * @return - A map containing all table schemas in the default and in the * user configuration. */ public FieldConfigurationManager getFieldConfigs() { return fieldConfigs; } /** * @return the activeDataTable */ public String getActiveDataTable() { return activeDataTable; } public String getActiveDataSchema() { return activeDataSchema; } /** * @return the activeSchemaName */ public String getActiveSchemaName() { return activeSchemaName; } /** * @return the mergedConfigData */ public byte[] getMergedConfigData() { return mergedConfigData; } public List getTableSchemaNames() { return schemaNames; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy