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 :