Mercurial > dive4elements > gnv-client
diff gnv-artifacts/src/main/java/de/intevation/gnv/state/OutputStateBase.java @ 1119:7c4f81f74c47
merged gnv-artifacts
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:00 +0200 |
parents | dec4257ad570 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/OutputStateBase.java Fri Sep 28 12:14:00 2012 +0200 @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.gnv.state; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import javax.xml.xpath.XPathConstants; + +import net.sf.ehcache.Cache; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; +import de.intevation.gnv.artifacts.cache.CacheFactory; +import de.intevation.gnv.artifacts.ressource.RessourceFactory; +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.geobackend.base.query.QueryExecutor; +import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; +import de.intevation.gnv.geobackend.base.query.exception.QueryException; +import de.intevation.gnv.state.describedata.MinMaxDescribeData; +import de.intevation.gnv.state.exception.StateException; +import de.intevation.gnv.utils.InputValidator; + +/** + * This is the default implementation of <code>OutputState</code>. Artifacts + * having reached this state or a subclass of this state are able to produce + * some output (e.g. chart, histograms, statistic, etc). + * + * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a> + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + * + */ +public abstract class OutputStateBase +extends StateBase +implements OutputState +{ + public static final String XPATH_OUTPUT_MODE = + "/art:action/art:out/@name"; + + public static final String XPATH_EXPORT_MODE = + "/art:action/art:out/art:export/@name"; + + public static final String XPATH_MIME_TYPE = + "/art:action/art:out/art:mime-type/@value"; + + public static final String XPATH_EXPORTS = + "exportModes/export"; + + /** + * The UID of this Class + */ + private static final long serialVersionUID = -1718732895737303823L; + + /** + * the logger, used to log exceptions and additonaly information + */ + private static Logger log = Logger.getLogger(OutputStateBase.class); + + /** + * The different Outputmodes which are provided by an OutputState + */ + protected Collection<OutputMode> outputModes = null; + + protected String queryODVID = null; + + /** + * Constructor + */ + public OutputStateBase() { + super(); + } + + public Collection<OutputMode> getOutputModes() { + log.debug("OutputStateBase.getOutputModes"); + return this.outputModes; + } + + + @Override + public void setup(Node configuration) { + log.debug("OutputStateBase.setup"); + super.setup(configuration); + + this.queryODVID = Config.getStringXPath(configuration,"queryID-odv"); + + NodeList outputModeList = Config.getNodeSetXPath(configuration, + "outputsModes/outputsMode"); + if (outputModeList != null) { + log.debug(outputModeList.getLength() + " were found."); + this.outputModes = new ArrayList<OutputMode>(outputModeList + .getLength()); + for (int i = 0; i < outputModeList.getLength(); i++) { + Element currentNode = (Element)outputModeList.item(i); + String name = currentNode.getAttribute("name"); + String description =currentNode.getAttribute("description"); + String mimeType = currentNode.getAttribute("mime-type"); + NodeList inputValuesList = Config.getNodeSetXPath(currentNode, + "parameters/inputvalue"); + Collection<InputValue> inputParameters = null; + if (inputValuesList != null) { + inputParameters = new ArrayList<InputValue>(inputValuesList + .getLength()); + for (int j = 0; j < inputValuesList.getLength(); j++) { + Element currentInputValuesNode = (Element)inputValuesList.item(j); + String inputValueName = currentInputValuesNode.getAttribute("name"); + String inputValueType = currentInputValuesNode.getAttribute("type"); + String defaultValue =currentInputValuesNode.getAttribute("value"); + boolean isMultiselect = false; + InputValue inputValue = new DefaultInputValue( + inputValueName, inputValueType, defaultValue, + isMultiselect); + inputParameters.add(inputValue); + } + } + + // parse export modes + List<ExportMode> exportList = null; + NodeList exports = (NodeList) XMLUtils.xpath( + currentNode, XPATH_EXPORTS, XPathConstants.NODESET); + + if (exports != null) { + int exportSize = exports.getLength(); + + exportList = new ArrayList<ExportMode>(exportSize); + for (int k = 0; k < exportSize; k++) { + Element exp = (Element) exports.item(k); + String expName = exp.getAttribute("name"); + String expDesc = exp.getAttribute("description"); + String expMime = exp.getAttribute("mime-type"); + + exportList.add( + new DefaultExportMode(expName, expDesc, expMime)); + } + } + + OutputMode outputMode = new DefaultOutputMode(name, + description, mimeType, inputParameters, exportList); + log.debug(outputMode.toString()); + this.outputModes.add(outputMode); + + } + } + } + + + @Override + public void advance(String uuid, CallContext context) + throws StateException + { + } + + + @Override + public void initialize(String uuid, CallContext context) + throws StateException + { + } + + /** + * This method needs to be defined by concrete subclasses. Nothing is done + * here. + */ + public void out( + Document format, + Collection<InputData> inputData, + OutputStream outputStream, + String uuid, + CallMeta callMeta + ) + throws StateException + { + } + + /** + * This method needs to be defined by concrete subclasses. Nothing is done + * here. + */ + public void out(String outputMode, Collection<InputData> inputData, + OutputStream outputStream) throws StateException { + } + + /** + * Returns the data used to create charts. If a cache is configured, try to + * fetch the data from cache. The database is queried if the data is not in + * cache yet, or if no cache is configured. If the cache is configured, but + * the data is not in cache yet, put it into cache for a faster access + * in a later time. + * + * @param uuid The uuid of an artifact. + * @param callContext The CallContext. + * @return the chart data. + */ + protected Object getChartResult(String uuid, CallContext callContext) { + log.debug("OutputStateBase.getChartResult"); + CacheFactory factory = CacheFactory.getInstance(); + + if (factory.isInitialized()) { + // we use a cache + log.info("Using cache."); + Cache cache = factory.getCache(); + String key = "chart_" + getHash(); + + net.sf.ehcache.Element value = cache.get(key); + if (value != null) { + log.debug("Found element in cache."); + return value.getObjectValue(); + } + else { + log.debug("Element not in cache, we need to ask the database"); + Object result = getData(queryID); + cache.put(new net.sf.ehcache.Element(key, result)); + + return result; + } + } + else { + // we don't use a cache, so we have to query the database every + // single time + log.info("Not using a cache."); + return getData(queryID); + } + } + + /** + * This method should no longer be used, because it is not good to put a + * chart into cache. Parameter changes done by the user wouldn't be detected + * proper. + * @deprecated + */ + protected Object getChartFromCache(String uuid, CallContext callContext) { + log.debug("Fetch chart [" + uuid + "] from cache"); + CacheFactory cacheFactory = CacheFactory.getInstance(); + if (cacheFactory.isInitialized()) { + String key = "chart_" + getHash(); + net.sf.ehcache.Element object = cacheFactory.getCache().get(key); + + if (object != null) { + return object.getObjectValue(); + } + } + return null; + } + + /** + * Retrieves the data used to create an ODV export. + * + * @param uuid + * @return odv data. + */ + protected Collection<Result> getODVResult(String uuid) { + log.debug("OutputStateBase.getODVResult"); + // TODO add Caching? I think it's not nessessary + Collection<Result> returnValue = null; + if (this.queryODVID != null){ + returnValue = this.getData(this.queryODVID); + }else{ + log.warn("No Query for ODV Data is defined."); + } + return returnValue; + } + + /** + * Retrieve data from database with help of queryID. + * + * @param queryID A query id defined in sql statements properties file. + * @return some data. + */ + protected Collection<Result> getData(String queryID) { + log.debug("OutputStateBase.getData"); + Collection<Result> returnValue = null; + try { + String[] filterValues = this.generateFilterValuesFromInputData(); + try { + QueryExecutor queryExecutor = QueryExecutorFactory + .getInstance() + .getQueryExecutor(); + returnValue = queryExecutor.executeQuery(queryID,filterValues); + } catch (RuntimeException e) { + log.error(e, e); + } + } catch (QueryException e) { + log.error(e, e); + } + return returnValue; + } + + /** + * This method removes the data used for creating charts from cache. + * + */ + protected void removeChartResult(String uuid) { + log.debug("OutputStateBase.getChartResult"); + if (CacheFactory.getInstance().isInitialized()) { + String key = "chart_" + getHash(); + log.debug("Hash for Queryelements: " + key); + net.sf.ehcache.Element value = CacheFactory.getInstance().getCache().get(key); + if (value != null) { + CacheFactory.getInstance().getCache().remove(key); + } + } + } + + /** + * This method should no longer be used. It removes a chart from cache. + * + * @deprecated + */ + protected void removeChart(String uuid) { + log.debug("OutputStateBase.removeChart from cache"); + + CacheFactory cacheFactory = CacheFactory.getInstance(); + if (cacheFactory.isInitialized()) { + String key = "chart_" + getHash(); + net.sf.ehcache.Element object = cacheFactory.getCache().get(key); + if (object != null) + cacheFactory.getCache().remove(key); + } + } + + /** + * This is an internal method used while database query. + */ + protected void purifyChart(Object chart, String uuid) { + log.debug("Prufify chart [" + uuid + "]"); + CacheFactory cacheFactory = CacheFactory.getInstance(); + if (cacheFactory.isInitialized()) { + String key = "chart_" + getHash(); + cacheFactory.getCache().put(new net.sf.ehcache.Element(key, chart)); + } + } + + + /** + * Use this method to feed a state with new data. + */ + @Override + public Document feed( + CallContext context, + Collection<InputData> inputData, + String uuid) + throws StateException + { + putInputData(inputData, uuid); + + return feedSuccess(); + } + + /** + * This method is used to put new data into a next state. The difference + * between this method and feed is, that this method should be used to + * transfer some old input data required by this state. New data need to be + * inserted via feed! + */ + @Override + public void putInputData(Collection<InputData> inputData, + String uuid) + throws StateException { + log.debug("OutputStateBase.putInputData"); + this.removeChartResult(uuid); + this.removeChart(uuid); + + if (inputData != null) { + Iterator<InputData> it = inputData.iterator(); + InputValidator iv = new InputValidator(); + while (it.hasNext()) { + InputData tmpItem = it.next(); + Object tmpObj = tmpItem.getObject(); + InputValue inputValue = this.inputValues.get(tmpItem.getName()); + if (inputValue != null) { + if (this.inputData == null) { + this.inputData = new HashMap<String, InputData>( + inputData.size()); + } + + boolean valid = InputValidator.isInputValid(tmpItem.getValue(), + inputValue.getType()); + if (valid) { + if (tmpItem.getName().equals(MINVALUEFIELDNAME)){ + String minValue = tmpItem.getValue(); + String maxValue = getInputValue4ID(inputData, MAXVALUEFIELDNAME); + valid = InputValidator.isInputValid(maxValue,inputValue.getType()); + if (!valid){ + String errMsg = "Wrong input for " + tmpItem.getValue() + + " is not an " + inputValue.getType() + + " Value."; + log.warn(errMsg); + throw new StateException(errMsg); + } + + valid = InputValidator.isInputValid(minValue, + maxValue, + inputValue.getType()); + if (!valid){ + String errMsg = "MaxValue-Input is less than MinValue-Input "; + log.warn(errMsg); + throw new StateException(errMsg); + } + }else if (tmpItem.getName().equals(MAXVALUEFIELDNAME)){ + String minValue = getInputValue4ID(inputData, MINVALUEFIELDNAME); + String maxValue = tmpItem.getValue(); + valid = InputValidator.isInputValid(minValue,inputValue.getType()); + if (!valid){ + String errMsg = "Wrong input for " + tmpItem.getValue() + + " is not an " + inputValue.getType() + + " Value."; + log.warn(errMsg); + throw new StateException(errMsg); + } + + valid = InputValidator.isInputValid(minValue, + maxValue, + inputValue.getType()); + if (!valid){ + String errMsg = "MaxValue-Input is less than MinValue-Input "; + log.warn(errMsg); + throw new StateException(errMsg); + } + } + this.inputData.put(tmpItem.getName(), tmpItem); + } else { + String errMsg = "Wrong input for " + tmpItem.getValue() + + " is not an " + inputValue.getType() + + " Value."; + log.warn(errMsg); + throw new StateException(errMsg); + } + + } + else if (tmpObj != null && tmpObj instanceof MinMaxDescribeData) { + MinMaxDescribeData data = (MinMaxDescribeData) tmpObj; + if (this.inputData == null) { + this.inputData = new HashMap<String, InputData>(inputData.size()); + } + this.inputData.put(tmpItem.getName(), tmpItem); + this.inputData.put("minvalue", new DefaultInputData("minvalue", (String) data.getMinValue())); + this.inputData.put("maxvalue", new DefaultInputData("maxvalue", (String) data.getMaxValue())); + } + else { + + String errMsg = "No Inputvalue given for Inputdata " + + tmpItem.getName(); + log.warn(errMsg + "Value will be ignored"); + + } + } + } else { + log.warn("No Inputdata given"); + } + setHash(uuid); + } + + + public void out( + String outputMode, + Collection<InputData> inputData, + OutputStream outputStream, + String uuid, + CallMeta callMeta) + throws StateException { } + + + /** + * Retrieves a message from resource bundle specified by locale. + * + * @param locale Locale to use. + * @param key The key of the message. + * @param value The default value. + * @return The value. + */ + protected String getMessage(Locale locale, String key, String value) { + return RessourceFactory.getInstance().getRessource( + locale, + key, + value + ); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :