/*
* Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com>
*
* Licensed 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.
*/
package com.obidea.semantika.app;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.obidea.semantika.exception.ConfigurationException;
import com.obidea.semantika.exception.ResourceNotFoundException;
import com.obidea.semantika.exception.SemantikaException;
import com.obidea.semantika.util.ConfigHelper;
import com.obidea.semantika.util.LogUtils;
import com.obidea.semantika.util.StringUtils;
import com.obidea.semantika.util.XmlHelper;
public class ApplicationFactory
{
public static final String DEFAULT_CONFIGURATION_FILENAME = "application.cfg.xml"; //$NON-NLS-1$
private PropertiesConfiguration mProperties;
private SettingFactory mSettingFactory;
private XmlHelper mXmlHelper;
private static final Logger LOG = LogUtils.createLogger("semantika.application"); //$NON-NLS-1$
protected ApplicationFactory(SettingFactory factory)
{
LOG.debug("Initializing ApplicationFactory."); //$NON-NLS-1$
mSettingFactory = factory;
mXmlHelper = new XmlHelper();
mProperties = Environment.getProperties();
}
public ApplicationFactory()
{
this(new DefaultSettingFactory());
}
public ApplicationFactory addProperty(String propertyName, Object value)
{
mProperties.setProperty(propertyName, value);
return this;
}
public ApplicationFactory setName(String name)
{
mProperties.setProperty(Environment.APPLICATION_FACTORY_NAME, name);
return this;
}
public ApplicationFactory setOntologySource(String resource)
{
mProperties.setProperty(Environment.ONTOLOGY_SOURCE, resource);
return this;
}
public ApplicationFactory addMappingSource(String resource)
{
return addMappingSource(resource, true);
}
public ApplicationFactory addMappingSource(String resource, boolean useStrictParsing)
{
mProperties.addProperty(Environment.MAPPING_SOURCE, resource);
mProperties.addProperty(Environment.STRICT_PARSING, useStrictParsing);
return this;
}
public ApplicationFactory configure() throws ConfigurationException
{
return configure(DEFAULT_CONFIGURATION_FILENAME); //$NON-NLS-1$
}
public ApplicationFactory configure(String resource) throws ConfigurationException
{
LOG.debug("Reading configuration (path: {})", resource); //$NON-NLS-1$
InputStream stream = ConfigHelper.getResourceInputStream(resource);
return doConfigure(stream, resource);
}
public ApplicationFactory configure(File resource) throws ConfigurationException
{
LOG.debug("Reading configuration (file: {})", resource.getName()); //$NON-NLS-1$
try {
InputStream stream = new FileInputStream(resource);
return doConfigure(stream, resource.toString());
}
catch (FileNotFoundException e) {
throw new ResourceNotFoundException(resource + " is not found", e); //$NON-NLS-1$
}
}
public ApplicationFactory configure(URL resource) throws ConfigurationException
{
LOG.debug("Reading configuration (url: {})" + resource.toString()); //$NON-NLS-1$
try {
InputStream stream = resource.openStream();
return doConfigure(stream, resource.toString());
}
catch (IOException e) {
throw new ResourceNotFoundException(resource + " is not found", e); //$NON-NLS-1$
}
}
public ApplicationManager createApplicationManager()
{
LOG.info("Initializing ApplicationManager."); //$NON-NLS-1$
try {
debugConfigurationProperties();
Environment.verify(mProperties);
PropertiesConfiguration copy = new PropertiesConfiguration();
copy.append(mProperties);
Settings settings = buildSettings(copy);
return new ApplicationManager(settings);
}
catch (SemantikaException e) {
throw new ApplicationStartupException("Failed to create ApplicationManager", e); //$NON-NLS-1$
}
}
private void debugConfigurationProperties()
{
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.APPLICATION_FACTORY_NAME,
mProperties.getString(Environment.APPLICATION_FACTORY_NAME));
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.CONNECTION_URL,
mProperties.getString(Environment.CONNECTION_URL));
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.TRANSACTION_TIMEOUT,
mProperties.getString(Environment.TRANSACTION_TIMEOUT));
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.TRANSACTION_FETCH_SIZE,
mProperties.getString(Environment.TRANSACTION_FETCH_SIZE));
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.TRANSACTION_MAX_ROWS,
mProperties.getString(Environment.TRANSACTION_MAX_ROWS));
LOG.debug("* {} = {}", //$NON-NLS-1$
Environment.ONTOLOGY_SOURCE,
mProperties.getString(Environment.ONTOLOGY_SOURCE));
String[] mappingSources = mProperties.getStringArray(Environment.MAPPING_SOURCE);
String[] useStrictParsing = mProperties.getStringArray(Environment.STRICT_PARSING);
for (int i = 0; i < mappingSources.length; i++) {
LOG.debug("* {} = {} (strict-parsing={})", //$NON-NLS-1$
Environment.MAPPING_SOURCE,
mappingSources[i], useStrictParsing[i]);
}
}
private Settings buildSettings(PropertiesConfiguration properties) throws SemantikaException
{
Settings settings = mSettingFactory.buildSettings(properties);
return settings;
}
protected ApplicationFactory doConfigure(InputStream stream, String resourceName) throws ConfigurationException
{
Document doc = null;
try {
List<SAXParseException> errorList = new ArrayList<SAXParseException>();
doc = mXmlHelper.createDocumentBuilder(resourceName, errorList, XmlHelper.DEFAULT_DTD_RESOLVER).parse(stream);
if (errorList.size() != 0) {
throw new ConfigurationException("Invalid configuration", errorList.get(0)); //$NON-NLS-1$
}
}
catch (SAXException e) {
throw new ConfigurationException("Syntax error", e); //$NON-NLS-1$
}
catch (IOException e) {
throw new ConfigurationException("Configuration loading error", e); //$NON-NLS-1$
}
catch (ParserConfigurationException e) {
throw new ConfigurationException("Configuration error", e); //$NON-NLS-1$
}
finally {
try {
stream.close();
}
catch (IOException e) {
LOG.warn("Could not close input stream {}", resourceName); //$NON-NLS-1$
LOG.warn("Detailed cause: {}", e.getMessage()); //$NON-NLS-1$
}
}
return doConfigure(doc);
}
protected ApplicationFactory doConfigure(Document doc) throws ConfigurationException
{
/*
* Optional but recommended
* http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
*/
doc.getDocumentElement().normalize();
Element afNode = getElementByTagName(doc, "application-factory"); //$NON-NLS-1$
addApplicationFactoryProperties(afNode);
Element dsNode = getElementByTagName(doc, "data-source"); //$NON-NLS-1$
addDataSourceProperties(dsNode);
Element osNode = getElementByTagName(doc, "ontology-source"); //$NON-NLS-1$
addOntologyResource(osNode);
NodeList msNodeList = getElementsByTagName(doc, "mapping-source"); //$NON-NLS-1$
addMappingResource(msNodeList);
LOG.debug("ApplicationFactory is sucessfully created."); //$NON-NLS-1$
return this;
}
private void addApplicationFactoryProperties(Element element)
{
String name = element.getAttribute("name"); //$NON-NLS-1$
if (!StringUtils.isEmpty(name)) {
mProperties.setProperty(Environment.APPLICATION_FACTORY_NAME, name);
}
}
private static NodeList getElementsByTagName(Document doc, String elementName)
{
return doc.getElementsByTagName(elementName);
}
private static Element getElementByTagName(Document doc, String elementName)
{
return (Element) getElementsByTagName(doc, elementName).item(0);
}
private void addDataSourceProperties(Element parent)
{
NodeList properties = parent.getChildNodes();
for (int i = 0; i < properties.getLength(); i++) {
Node childNode = properties.item(i);
if (childNode instanceof Element) {
Element node = (Element) properties.item(i);
String name = node.getAttribute("name"); //$NON-NLS-1$
String value = node.getTextContent();
mProperties.setProperty(name, value);
}
}
}
private void addOntologyResource(Element parent)
{
/*
* The ontology-source element is an optional. The system will prepare an
* empty ontology if users don't specify one.
*/
if (parent != null) {
String value = parent.getAttribute("resource"); //$NON-NLS-1$
mProperties.setProperty(Environment.ONTOLOGY_SOURCE, value);
}
}
private void addMappingResource(NodeList list)
{
for (int i = 0; i < list.getLength(); i++) {
Element parent = (Element) list.item(i);
String value = parent.getAttribute("resource"); //$NON-NLS-1$
mProperties.addProperty(Environment.MAPPING_SOURCE, value);
String isStrict = parent.getAttribute("strict-parsing"); //$NON-NLS-1$
mProperties.addProperty(Environment.STRICT_PARSING, isStrict);
}
}
public PropertiesConfiguration getProperties()
{
return mProperties;
}
}