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