comparison 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
comparison
equal deleted inserted replaced
1027:fca4b5eb8d2f 1119:7c4f81f74c47
1 /*
2 * Copyright (c) 2010 by Intevation GmbH
3 *
4 * This program is free software under the LGPL (>=v2.1)
5 * Read the file LGPL.txt coming with the software for details
6 * or visit http://www.gnu.org/licenses/ if it does not exist.
7 */
8
9 package de.intevation.gnv.state;
10
11 import java.io.OutputStream;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Locale;
18
19 import javax.xml.xpath.XPathConstants;
20
21 import net.sf.ehcache.Cache;
22
23 import org.apache.log4j.Logger;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.Element;
26 import org.w3c.dom.Node;
27 import org.w3c.dom.NodeList;
28
29 import de.intevation.artifacts.common.utils.Config;
30 import de.intevation.artifacts.common.utils.XMLUtils;
31 import de.intevation.artifacts.CallContext;
32 import de.intevation.artifacts.CallMeta;
33 import de.intevation.gnv.artifacts.cache.CacheFactory;
34 import de.intevation.gnv.artifacts.ressource.RessourceFactory;
35 import de.intevation.gnv.geobackend.base.Result;
36 import de.intevation.gnv.geobackend.base.query.QueryExecutor;
37 import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
38 import de.intevation.gnv.geobackend.base.query.exception.QueryException;
39 import de.intevation.gnv.state.describedata.MinMaxDescribeData;
40 import de.intevation.gnv.state.exception.StateException;
41 import de.intevation.gnv.utils.InputValidator;
42
43 /**
44 * This is the default implementation of <code>OutputState</code>. Artifacts
45 * having reached this state or a subclass of this state are able to produce
46 * some output (e.g. chart, histograms, statistic, etc).
47 *
48 * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a>
49 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
50 *
51 */
52 public abstract class OutputStateBase
53 extends StateBase
54 implements OutputState
55 {
56 public static final String XPATH_OUTPUT_MODE =
57 "/art:action/art:out/@name";
58
59 public static final String XPATH_EXPORT_MODE =
60 "/art:action/art:out/art:export/@name";
61
62 public static final String XPATH_MIME_TYPE =
63 "/art:action/art:out/art:mime-type/@value";
64
65 public static final String XPATH_EXPORTS =
66 "exportModes/export";
67
68 /**
69 * The UID of this Class
70 */
71 private static final long serialVersionUID = -1718732895737303823L;
72
73 /**
74 * the logger, used to log exceptions and additonaly information
75 */
76 private static Logger log = Logger.getLogger(OutputStateBase.class);
77
78 /**
79 * The different Outputmodes which are provided by an OutputState
80 */
81 protected Collection<OutputMode> outputModes = null;
82
83 protected String queryODVID = null;
84
85 /**
86 * Constructor
87 */
88 public OutputStateBase() {
89 super();
90 }
91
92 public Collection<OutputMode> getOutputModes() {
93 log.debug("OutputStateBase.getOutputModes");
94 return this.outputModes;
95 }
96
97
98 @Override
99 public void setup(Node configuration) {
100 log.debug("OutputStateBase.setup");
101 super.setup(configuration);
102
103 this.queryODVID = Config.getStringXPath(configuration,"queryID-odv");
104
105 NodeList outputModeList = Config.getNodeSetXPath(configuration,
106 "outputsModes/outputsMode");
107 if (outputModeList != null) {
108 log.debug(outputModeList.getLength() + " were found.");
109 this.outputModes = new ArrayList<OutputMode>(outputModeList
110 .getLength());
111 for (int i = 0; i < outputModeList.getLength(); i++) {
112 Element currentNode = (Element)outputModeList.item(i);
113 String name = currentNode.getAttribute("name");
114 String description =currentNode.getAttribute("description");
115 String mimeType = currentNode.getAttribute("mime-type");
116 NodeList inputValuesList = Config.getNodeSetXPath(currentNode,
117 "parameters/inputvalue");
118 Collection<InputValue> inputParameters = null;
119 if (inputValuesList != null) {
120 inputParameters = new ArrayList<InputValue>(inputValuesList
121 .getLength());
122 for (int j = 0; j < inputValuesList.getLength(); j++) {
123 Element currentInputValuesNode = (Element)inputValuesList.item(j);
124 String inputValueName = currentInputValuesNode.getAttribute("name");
125 String inputValueType = currentInputValuesNode.getAttribute("type");
126 String defaultValue =currentInputValuesNode.getAttribute("value");
127 boolean isMultiselect = false;
128 InputValue inputValue = new DefaultInputValue(
129 inputValueName, inputValueType, defaultValue,
130 isMultiselect);
131 inputParameters.add(inputValue);
132 }
133 }
134
135 // parse export modes
136 List<ExportMode> exportList = null;
137 NodeList exports = (NodeList) XMLUtils.xpath(
138 currentNode, XPATH_EXPORTS, XPathConstants.NODESET);
139
140 if (exports != null) {
141 int exportSize = exports.getLength();
142
143 exportList = new ArrayList<ExportMode>(exportSize);
144 for (int k = 0; k < exportSize; k++) {
145 Element exp = (Element) exports.item(k);
146 String expName = exp.getAttribute("name");
147 String expDesc = exp.getAttribute("description");
148 String expMime = exp.getAttribute("mime-type");
149
150 exportList.add(
151 new DefaultExportMode(expName, expDesc, expMime));
152 }
153 }
154
155 OutputMode outputMode = new DefaultOutputMode(name,
156 description, mimeType, inputParameters, exportList);
157 log.debug(outputMode.toString());
158 this.outputModes.add(outputMode);
159
160 }
161 }
162 }
163
164
165 @Override
166 public void advance(String uuid, CallContext context)
167 throws StateException
168 {
169 }
170
171
172 @Override
173 public void initialize(String uuid, CallContext context)
174 throws StateException
175 {
176 }
177
178 /**
179 * This method needs to be defined by concrete subclasses. Nothing is done
180 * here.
181 */
182 public void out(
183 Document format,
184 Collection<InputData> inputData,
185 OutputStream outputStream,
186 String uuid,
187 CallMeta callMeta
188 )
189 throws StateException
190 {
191 }
192
193 /**
194 * This method needs to be defined by concrete subclasses. Nothing is done
195 * here.
196 */
197 public void out(String outputMode, Collection<InputData> inputData,
198 OutputStream outputStream) throws StateException {
199 }
200
201 /**
202 * Returns the data used to create charts. If a cache is configured, try to
203 * fetch the data from cache. The database is queried if the data is not in
204 * cache yet, or if no cache is configured. If the cache is configured, but
205 * the data is not in cache yet, put it into cache for a faster access
206 * in a later time.
207 *
208 * @param uuid The uuid of an artifact.
209 * @param callContext The CallContext.
210 * @return the chart data.
211 */
212 protected Object getChartResult(String uuid, CallContext callContext) {
213 log.debug("OutputStateBase.getChartResult");
214 CacheFactory factory = CacheFactory.getInstance();
215
216 if (factory.isInitialized()) {
217 // we use a cache
218 log.info("Using cache.");
219 Cache cache = factory.getCache();
220 String key = "chart_" + getHash();
221
222 net.sf.ehcache.Element value = cache.get(key);
223 if (value != null) {
224 log.debug("Found element in cache.");
225 return value.getObjectValue();
226 }
227 else {
228 log.debug("Element not in cache, we need to ask the database");
229 Object result = getData(queryID);
230 cache.put(new net.sf.ehcache.Element(key, result));
231
232 return result;
233 }
234 }
235 else {
236 // we don't use a cache, so we have to query the database every
237 // single time
238 log.info("Not using a cache.");
239 return getData(queryID);
240 }
241 }
242
243 /**
244 * This method should no longer be used, because it is not good to put a
245 * chart into cache. Parameter changes done by the user wouldn't be detected
246 * proper.
247 * @deprecated
248 */
249 protected Object getChartFromCache(String uuid, CallContext callContext) {
250 log.debug("Fetch chart [" + uuid + "] from cache");
251 CacheFactory cacheFactory = CacheFactory.getInstance();
252 if (cacheFactory.isInitialized()) {
253 String key = "chart_" + getHash();
254 net.sf.ehcache.Element object = cacheFactory.getCache().get(key);
255
256 if (object != null) {
257 return object.getObjectValue();
258 }
259 }
260 return null;
261 }
262
263 /**
264 * Retrieves the data used to create an ODV export.
265 *
266 * @param uuid
267 * @return odv data.
268 */
269 protected Collection<Result> getODVResult(String uuid) {
270 log.debug("OutputStateBase.getODVResult");
271 // TODO add Caching? I think it's not nessessary
272 Collection<Result> returnValue = null;
273 if (this.queryODVID != null){
274 returnValue = this.getData(this.queryODVID);
275 }else{
276 log.warn("No Query for ODV Data is defined.");
277 }
278 return returnValue;
279 }
280
281 /**
282 * Retrieve data from database with help of queryID.
283 *
284 * @param queryID A query id defined in sql statements properties file.
285 * @return some data.
286 */
287 protected Collection<Result> getData(String queryID) {
288 log.debug("OutputStateBase.getData");
289 Collection<Result> returnValue = null;
290 try {
291 String[] filterValues = this.generateFilterValuesFromInputData();
292 try {
293 QueryExecutor queryExecutor = QueryExecutorFactory
294 .getInstance()
295 .getQueryExecutor();
296 returnValue = queryExecutor.executeQuery(queryID,filterValues);
297 } catch (RuntimeException e) {
298 log.error(e, e);
299 }
300 } catch (QueryException e) {
301 log.error(e, e);
302 }
303 return returnValue;
304 }
305
306 /**
307 * This method removes the data used for creating charts from cache.
308 *
309 */
310 protected void removeChartResult(String uuid) {
311 log.debug("OutputStateBase.getChartResult");
312 if (CacheFactory.getInstance().isInitialized()) {
313 String key = "chart_" + getHash();
314 log.debug("Hash for Queryelements: " + key);
315 net.sf.ehcache.Element value = CacheFactory.getInstance().getCache().get(key);
316 if (value != null) {
317 CacheFactory.getInstance().getCache().remove(key);
318 }
319 }
320 }
321
322 /**
323 * This method should no longer be used. It removes a chart from cache.
324 *
325 * @deprecated
326 */
327 protected void removeChart(String uuid) {
328 log.debug("OutputStateBase.removeChart from cache");
329
330 CacheFactory cacheFactory = CacheFactory.getInstance();
331 if (cacheFactory.isInitialized()) {
332 String key = "chart_" + getHash();
333 net.sf.ehcache.Element object = cacheFactory.getCache().get(key);
334 if (object != null)
335 cacheFactory.getCache().remove(key);
336 }
337 }
338
339 /**
340 * This is an internal method used while database query.
341 */
342 protected void purifyChart(Object chart, String uuid) {
343 log.debug("Prufify chart [" + uuid + "]");
344 CacheFactory cacheFactory = CacheFactory.getInstance();
345 if (cacheFactory.isInitialized()) {
346 String key = "chart_" + getHash();
347 cacheFactory.getCache().put(new net.sf.ehcache.Element(key, chart));
348 }
349 }
350
351
352 /**
353 * Use this method to feed a state with new data.
354 */
355 @Override
356 public Document feed(
357 CallContext context,
358 Collection<InputData> inputData,
359 String uuid)
360 throws StateException
361 {
362 putInputData(inputData, uuid);
363
364 return feedSuccess();
365 }
366
367 /**
368 * This method is used to put new data into a next state. The difference
369 * between this method and feed is, that this method should be used to
370 * transfer some old input data required by this state. New data need to be
371 * inserted via feed!
372 */
373 @Override
374 public void putInputData(Collection<InputData> inputData,
375 String uuid)
376 throws StateException {
377 log.debug("OutputStateBase.putInputData");
378 this.removeChartResult(uuid);
379 this.removeChart(uuid);
380
381 if (inputData != null) {
382 Iterator<InputData> it = inputData.iterator();
383 InputValidator iv = new InputValidator();
384 while (it.hasNext()) {
385 InputData tmpItem = it.next();
386 Object tmpObj = tmpItem.getObject();
387 InputValue inputValue = this.inputValues.get(tmpItem.getName());
388 if (inputValue != null) {
389 if (this.inputData == null) {
390 this.inputData = new HashMap<String, InputData>(
391 inputData.size());
392 }
393
394 boolean valid = InputValidator.isInputValid(tmpItem.getValue(),
395 inputValue.getType());
396 if (valid) {
397 if (tmpItem.getName().equals(MINVALUEFIELDNAME)){
398 String minValue = tmpItem.getValue();
399 String maxValue = getInputValue4ID(inputData, MAXVALUEFIELDNAME);
400 valid = InputValidator.isInputValid(maxValue,inputValue.getType());
401 if (!valid){
402 String errMsg = "Wrong input for " + tmpItem.getValue()
403 + " is not an " + inputValue.getType()
404 + " Value.";
405 log.warn(errMsg);
406 throw new StateException(errMsg);
407 }
408
409 valid = InputValidator.isInputValid(minValue,
410 maxValue,
411 inputValue.getType());
412 if (!valid){
413 String errMsg = "MaxValue-Input is less than MinValue-Input ";
414 log.warn(errMsg);
415 throw new StateException(errMsg);
416 }
417 }else if (tmpItem.getName().equals(MAXVALUEFIELDNAME)){
418 String minValue = getInputValue4ID(inputData, MINVALUEFIELDNAME);
419 String maxValue = tmpItem.getValue();
420 valid = InputValidator.isInputValid(minValue,inputValue.getType());
421 if (!valid){
422 String errMsg = "Wrong input for " + tmpItem.getValue()
423 + " is not an " + inputValue.getType()
424 + " Value.";
425 log.warn(errMsg);
426 throw new StateException(errMsg);
427 }
428
429 valid = InputValidator.isInputValid(minValue,
430 maxValue,
431 inputValue.getType());
432 if (!valid){
433 String errMsg = "MaxValue-Input is less than MinValue-Input ";
434 log.warn(errMsg);
435 throw new StateException(errMsg);
436 }
437 }
438 this.inputData.put(tmpItem.getName(), tmpItem);
439 } else {
440 String errMsg = "Wrong input for " + tmpItem.getValue()
441 + " is not an " + inputValue.getType()
442 + " Value.";
443 log.warn(errMsg);
444 throw new StateException(errMsg);
445 }
446
447 }
448 else if (tmpObj != null && tmpObj instanceof MinMaxDescribeData) {
449 MinMaxDescribeData data = (MinMaxDescribeData) tmpObj;
450 if (this.inputData == null) {
451 this.inputData = new HashMap<String, InputData>(inputData.size());
452 }
453 this.inputData.put(tmpItem.getName(), tmpItem);
454 this.inputData.put("minvalue", new DefaultInputData("minvalue", (String) data.getMinValue()));
455 this.inputData.put("maxvalue", new DefaultInputData("maxvalue", (String) data.getMaxValue()));
456 }
457 else {
458
459 String errMsg = "No Inputvalue given for Inputdata "
460 + tmpItem.getName();
461 log.warn(errMsg + "Value will be ignored");
462
463 }
464 }
465 } else {
466 log.warn("No Inputdata given");
467 }
468 setHash(uuid);
469 }
470
471
472 public void out(
473 String outputMode,
474 Collection<InputData> inputData,
475 OutputStream outputStream,
476 String uuid,
477 CallMeta callMeta)
478 throws StateException { }
479
480
481 /**
482 * Retrieves a message from resource bundle specified by locale.
483 *
484 * @param locale Locale to use.
485 * @param key The key of the message.
486 * @param value The default value.
487 * @return The value.
488 */
489 protected String getMessage(Locale locale, String key, String value) {
490 return RessourceFactory.getInstance().getRessource(
491 locale,
492 key,
493 value
494 );
495 }
496 }
497 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org