Mercurial > dive4elements > gnv-client
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 : |