001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.action;
019:
020: import java.text.NumberFormat;
021: import java.text.ParseException;
022: import java.util.ArrayList;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.Map;
026:
027: import de.finix.contelligent.CallData;
028: import de.finix.contelligent.Component;
029: import de.finix.contelligent.ComponentCreationException;
030: import de.finix.contelligent.ComponentManager;
031: import de.finix.contelligent.ComponentNotFoundException;
032: import de.finix.contelligent.ComponentPathException;
033: import de.finix.contelligent.Container;
034: import de.finix.contelligent.components.Folder;
035: import de.finix.contelligent.logging.LoggingService;
036: import de.finix.contelligent.render.ActionRenderer;
037: import de.finix.contelligent.render.Renderer;
038:
039: /**
040: * An <code>Action</code> may perform complex tasks.
041: */
042: public abstract class AbstractAction extends Folder implements Action,
043: Constants {
044:
045: final static org.apache.log4j.Logger log = LoggingService
046: .getLogger(AbstractAction.class);
047:
048: protected final static String PARAMETER_DIR = "parameter/";
049:
050: protected final static String RESULT_DIR = "result/";
051:
052: protected final static String MESSAGE_DIR = "message/";
053:
054: protected final static String DATA_DIR = "data/";
055:
056: private String scope = SCOPE_REQUEST_STRING;
057:
058: private boolean autoValidate = true;
059:
060: private Renderer renderer;
061:
062: public void postCreate() throws Exception {
063: super .postCreate();
064: renderer = new ActionRenderer(this );
065: }
066:
067: public boolean isDynamic() {
068: return true;
069: }
070:
071: public Renderer getRenderer() {
072: return renderer;
073: }
074:
075: public void setStorageScope(String scope) {
076: this .scope = scope;
077: }
078:
079: public String getStorageScope() {
080: return scope;
081: }
082:
083: protected void setAutoValidate(boolean flag) {
084: autoValidate = flag;
085: }
086:
087: public String perform(CallData callData) throws ActionException {
088: resetErrors(callData);
089: if (!storeAndValidateParameters(callData)) {
090: return ROUTING_ERROR;
091: } else {
092: return doPerform(callData);
093: }
094: }
095:
096: /**
097: * Performs the action. Implement this method in your subclass.
098: *
099: * @param callData
100: * an <code>CallData</code> value
101: * @return a string representing the exit state of the action
102: * @exception de.finix.contelligent.action.ActionException
103: * if an error occurs
104: */
105: protected abstract String doPerform(CallData callData)
106: throws ActionException;
107:
108: protected Object getParameter(String name, CallData callData)
109: throws NoSuchParameterException {
110: try {
111: Container paramFolder = (Container) callData
112: .getActualManager().getSubcomponent(this ,
113: PARAMETER_DIR, callData);
114: Parameter parameter = (Parameter) callData
115: .getActualManager().getSubcomponent(paramFolder,
116: name, callData);
117: return parameter.getObject(callData);
118: } catch (ComponentNotFoundException e) {
119: throw new NoSuchParameterException("No parameter " + name);
120: } catch (ComponentPathException e) {
121: throw new NoSuchParameterException("No parameter " + name);
122: } catch (ClassCastException e) {
123: throw new NoSuchParameterException(
124: "Wrong type of component " + name
125: + ": not a Parameter");
126: }
127: }
128:
129: protected Parameter[] getRegisteredParameters(CallData callData)
130: throws NoSuchParameterException {
131: try {
132: ArrayList list = new ArrayList(20);
133: ComponentManager manager = ctx.getCallManager();
134: Container container = (Container) manager.getSubcomponent(
135: this , PARAMETER_DIR, callData);
136: Iterator i = container.getSubcomponentNames();
137: while (i.hasNext()) {
138: String name = (String) i.next();
139: Component obj = manager.getSubcomponent(container,
140: name, callData);
141: // Object obj = i.next();
142: if (obj instanceof Parameter) {
143: list.add(obj);
144: } else {
145: log
146: .warn("'"
147: + this
148: + "':getRegisteredParameters() - found component '"
149: + obj
150: + "' in parameter dir which is NO parameter!");
151: }
152: }
153: int size = list.size();
154: return (Parameter[]) list.toArray(new Parameter[size]);
155: } catch (ClassCastException e) {
156: throw new NoSuchParameterException("Subcomponent '"
157: + PARAMETER_DIR + "' of action '" + this
158: + "' is no container!");
159: } catch (ComponentNotFoundException e) {
160: throw new NoSuchParameterException("'" + PARAMETER_DIR
161: + "' directory not found beneath: " + this );
162: } catch (ComponentPathException e) {
163: throw new NoSuchParameterException("'" + PARAMETER_DIR
164: + "' directory not found beneath: " + this );
165: }
166: }
167:
168: /**
169: * Answer a String[] if the parameter is a String[], null otherwise.
170: */
171: protected String[] getStringParameterValues(String name,
172: CallData callData) throws NoSuchParameterException {
173: Object value = getParameter(name, callData);
174:
175: if (value instanceof String[]) {
176: return (String[]) value;
177: } else {
178: return null;
179: }
180: }
181:
182: /**
183: * Answer a String, if the parameter value is a String or a String[] of
184: * length 1, null otherwise
185: */
186: protected String getStringParameter(String name, CallData callData)
187: throws NoSuchParameterException {
188: Object value = getParameter(name, callData);
189:
190: if (value instanceof String) {
191: return (String) value;
192: } else {
193: if (value instanceof String[]) {
194: String[] tmp = (String[]) value;
195: if (tmp.length == 1) {
196: return tmp[0];
197: }
198: }
199: }
200: return null;
201: }
202:
203: protected void setResult(String name, Object value,
204: CallData callData) throws NoSuchResultException {
205: try {
206: Variable result = (Variable) ctx.getCallManager()
207: .getSubcomponent(this , RESULT_DIR + name, callData);
208: result.setObject(value, callData);
209: } catch (ComponentNotFoundException e) {
210: throw new NoSuchResultException("No result variable "
211: + name);
212: } catch (ComponentPathException e) {
213: throw new NoSuchResultException("No result variable "
214: + name);
215: } catch (ClassCastException e) {
216: throw new NoSuchResultException("Wrong type of component "
217: + name + ": not a Variable");
218: }
219: }
220:
221: protected Object getResult(String name, CallData callData)
222: throws NoSuchResultException {
223: try {
224: Variable result = (Variable) ctx.getCallManager()
225: .getSubcomponent(this , RESULT_DIR + name, callData);
226: return result.getObject(callData);
227: } catch (ComponentNotFoundException e) {
228: throw new NoSuchResultException("No parameter " + name);
229: } catch (ComponentPathException e) {
230: throw new NoSuchResultException("No parameter " + name);
231: } catch (ClassCastException e) {
232: throw new NoSuchResultException("Wrong type of component "
233: + name + ": not a Variable");
234: }
235: }
236:
237: protected Variable[] getResultVariables(CallData callData)
238: throws NoSuchResultException {
239: try {
240: ComponentManager manager = ctx.getCallManager();
241: Container resultDir = (Container) manager.getSubcomponent(
242: this , RESULT_DIR, callData);
243: Iterator i = resultDir.getSubcomponentNames();
244: ArrayList list = new ArrayList(8);
245: while (i.hasNext()) {
246: String name = (String) i.next();
247: list.add(manager.getSubcomponent(resultDir, name,
248: callData));
249: }
250:
251: Variable[] vars = new Variable[list.size()];
252:
253: if (log.isDebugEnabled()) {
254: log
255: .debug("getResultVariables() - returning result variable list: "
256: + list);
257: }
258: return (Variable[]) list.toArray(vars);
259: } catch (ComponentNotFoundException e) {
260: throw new NoSuchResultException("'" + RESULT_DIR
261: + "' directory not found beneath: " + this );
262: } catch (ComponentPathException e) {
263: throw new NoSuchResultException("'" + RESULT_DIR
264: + "' directory not found beneath: " + this );
265: } catch (ClassCastException e) {
266: throw new NoSuchResultException("Subcomponent '"
267: + RESULT_DIR + "' of action '" + this
268: + "' is no container!");
269: }
270: }
271:
272: protected String getMessage(String name, CallData callData)
273: throws ComponentPathException, ComponentNotFoundException {
274: Component msg = ctx.getCallManager().getSubcomponent(this ,
275: MESSAGE_DIR + name, callData);
276: return renderComponent(msg, callData);
277: }
278:
279: protected boolean storeAndValidateParameters(CallData callData)
280: throws NoSuchParameterException {
281: try {
282: ComponentManager manager = ctx.getCallManager();
283: Container parameterContainer = (Container) manager
284: .getSubcomponent(this , PARAMETER_DIR, callData);
285: Iterator parameters = parameterContainer
286: .getSubcomponentNames();
287: boolean answer = true;
288:
289: clearStoredValues(callData);
290: while (parameters.hasNext()) {
291: String name = (String) parameters.next();
292: Component tmp = manager.getSubcomponent(
293: parameterContainer, name, callData);
294:
295: if (tmp instanceof ActionParameter) {
296: ActionParameter parameter = (ActionParameter) tmp;
297: String paramName = parameter.getComponentContext()
298: .getPath().getName();
299: String[] paramValues = null;
300:
301: paramValues = callData
302: .getParameterValues(paramName);
303: if (paramValues == null || paramValues.length == 0) {
304: if (parameter.isRequired()) {
305: log
306: .error("'"
307: + this
308: + "': can't find value for required parameter "
309: + paramName);
310: throw new NoSuchParameterException(
311: "Can't find value for required parameter "
312: + paramName);
313: } else {
314: String[] defaultValues = new String[] { parameter
315: .getDefault() };
316: if (log.isDebugEnabled()) {
317: log
318: .debug("'"
319: + this
320: + "': using default for optional parameter '"
321: + paramName);
322: }
323: paramValues = defaultValues;
324: }
325: } else {
326: // removeType this param from standard map:
327: // stdParams.removeType(paramName);
328: if (log.isDebugEnabled()) {
329: log
330: .debug("'"
331: + this
332: + "':getParameterMap() - removed param '"
333: + paramName
334: + "' from standard param-map!");
335: }
336: }
337: if (log.isDebugEnabled()) {
338: log.debug("'" + this
339: + "':getParameterMap() - found "
340: + paramValues.length
341: + " value(s) for param '" + paramName
342: + "'.");
343: }
344: storeValue(paramName, paramValues, callData);
345: if (autoValidate) {
346: if (!parameter.validate(paramValues)) {
347: if (log.isDebugEnabled()) {
348: log
349: .debug("'"
350: + this
351: + "':getParameterMap() - found invalid parameter: '"
352: + paramName + "'.");
353: }
354: storeError(paramName,
355: getRegExpErrorMessage(paramName,
356: callData), callData);
357: answer = false;
358: }
359: }
360: }
361: }
362: return answer;
363: } catch (ComponentCreationException e) {
364: log
365: .error(
366: "'"
367: + this
368: + "':getParameterMap() - could not create parameter-map!",
369: e);
370: throw new NoSuchParameterException(
371: "Exception in resolving paramters", e);
372: } catch (ComponentPathException e) {
373: log
374: .error(
375: "'"
376: + this
377: + "':getParameterMap() - could not create parameter-map!",
378: e);
379: throw new NoSuchParameterException(
380: "Exception in resolving paramters", e);
381: } catch (ComponentNotFoundException e) {
382: log
383: .error(
384: "'"
385: + this
386: + "':getParameterMap() - could not create parameter-map!",
387: e);
388: throw new NoSuchParameterException(
389: "Exception in resolving paramters", e);
390: }
391: }
392:
393: protected boolean validateParameters(CallData callData)
394: throws NoSuchParameterException {
395: try {
396: ComponentManager manager = ctx.getCallManager();
397: Container parameterContainer = (Container) manager
398: .getSubcomponent(this , PARAMETER_DIR, callData);
399: Iterator parameters = parameterContainer
400: .getSubcomponentNames();
401: boolean answer = true;
402:
403: while (parameters.hasNext()) {
404: String name = (String) parameters.next();
405: Object tmp = manager.getSubcomponent(
406: parameterContainer, name, callData);
407:
408: if (tmp instanceof ActionParameter) {
409: ActionParameter parameter = (ActionParameter) tmp;
410: String paramName = parameter.getComponentContext()
411: .getPath().getName();
412: String[] paramValues = null;
413:
414: paramValues = callData
415: .getParameterValues(paramName);
416: if (paramValues == null || paramValues.length == 0) {
417: if (parameter.isRequired()) {
418: log
419: .error("'"
420: + this
421: + "': can't find value for required parameter '"
422: + paramName);
423: throw new NoSuchParameterException(
424: "Can't find value for required parameter "
425: + paramName);
426: } else {
427: String[] defaultValues = new String[] { parameter
428: .getDefault() };
429: if (log.isDebugEnabled()) {
430: log
431: .debug("'"
432: + this
433: + "': using default for optional parameter '"
434: + paramName);
435: }
436: paramValues = defaultValues;
437: }
438: } else {
439: // removeType this param from standard map:
440: // stdParams.removeType(paramName);
441: if (log.isDebugEnabled()) {
442: log
443: .debug("'"
444: + this
445: + "':getParameterMap() - removed param '"
446: + paramName
447: + "' from standard param-map!");
448: }
449: }
450: if (log.isDebugEnabled()) {
451: log.debug("'" + this
452: + "':getParameterMap() - found "
453: + paramValues.length
454: + " value(s) for param '" + paramName
455: + "'.");
456: }
457: if (!parameter.validate(paramValues)) {
458: if (log.isDebugEnabled()) {
459: log
460: .debug("'"
461: + this
462: + "':getParameterMap() - found invalid parameter: '"
463: + paramName + "'.");
464: }
465: storeError(paramName, getRegExpErrorMessage(
466: paramName, callData), callData);
467: answer = false;
468: }
469: }
470: }
471: return answer;
472: } catch (ComponentCreationException e) {
473: log
474: .error(
475: "'"
476: + this
477: + "':getParameterMap() - could not create parameter-map!",
478: e);
479: throw new NoSuchParameterException(
480: "Exception in resolving paramters", e);
481: } catch (ComponentPathException e) {
482: log
483: .error(
484: "'"
485: + this
486: + "':getParameterMap() - could not create parameter-map!",
487: e);
488: throw new NoSuchParameterException(
489: "Exception in resolving paramters", e);
490: } catch (ComponentNotFoundException e) {
491: log
492: .error(
493: "'"
494: + this
495: + "':getParameterMap() - could not create parameter-map!",
496: e);
497: throw new NoSuchParameterException(
498: "Exception in resolving paramters", e);
499: }
500: }
501:
502: protected void clearStoredValues(CallData callData) {
503: if (scope.equals(SCOPE_REQUEST_STRING)) {
504: callData.removeRequestAttribute(VALUE_STORE);
505: } else {
506: callData.removeSessionAttribute(VALUE_STORE);
507: }
508: }
509:
510: protected void storeValue(String name, Object value,
511: CallData callData) {
512: Map values;
513: Object val = value;
514:
515: if (value instanceof String[]) {
516: String[] tmp = (String[]) value;
517: if (tmp.length == 1) {
518: val = tmp[0];
519: }
520: }
521:
522: if (scope.equals(SCOPE_REQUEST_STRING)) {
523: values = (Map) callData.getRequestAttribute(VALUE_STORE);
524: if (values == null) {
525: values = new HashMap();
526: callData.setRequestAttribute(VALUE_STORE, values);
527: }
528: } else {
529: values = (Map) callData.getSessionAttribute(VALUE_STORE);
530: if (values == null) {
531: values = new HashMap();
532: callData.setSessionAttribute(VALUE_STORE, values);
533: }
534: }
535: values.put(name, val);
536: }
537:
538: protected void storeError(String name, String value,
539: CallData callData) {
540: log.error("storing error :" + name + "=" + value);
541: getErrorMap(callData).put(name, value);
542: }
543:
544: protected void resetErrors(CallData callData) {
545: if (scope.equals(SCOPE_REQUEST_STRING)) {
546: callData.setRequestAttribute(ERROR_STORE, new HashMap());
547: } else {
548: callData.setSessionAttribute(ERROR_STORE, new HashMap());
549: }
550: }
551:
552: protected void remapError(String from, String to, CallData callData) {
553: Map errors = getErrorMap(callData);
554: Object value = errors.get(from);
555:
556: if (value != null) {
557: errors.remove(from);
558: errors.put(to, value);
559: }
560: }
561:
562: protected Map getErrorMap(CallData callData) {
563: Map values;
564:
565: if (scope.equals(SCOPE_REQUEST_STRING)) {
566: values = (Map) callData.getRequestAttribute(ERROR_STORE);
567: if (values == null) {
568: values = new HashMap();
569: callData.setRequestAttribute(ERROR_STORE, values);
570: }
571: } else {
572: values = (Map) callData.getSessionAttribute(ERROR_STORE);
573: if (values == null) {
574: values = new HashMap();
575: callData.setSessionAttribute(ERROR_STORE, values);
576: }
577: }
578: return values;
579: }
580:
581: protected String getRegExpErrorMessage(String paramName,
582: CallData callData) {
583: if (paramName == null) {
584: return REGEXP_ERROR;
585: }
586: try {
587: Component errorMessage = ctx
588: .getCallManager()
589: .getSubcomponent(
590: this ,
591: PARAMETER_DIR + paramName + "/errorMessage",
592: callData);
593: return renderComponent(errorMessage, callData);
594: } catch (ComponentNotFoundException e) {
595: return REGEXP_ERROR;
596: } catch (ComponentPathException e) {
597: return REGEXP_ERROR;
598: }
599: }
600:
601: protected Component getData(String name, CallData callData) {
602: try {
603: return ctx.getCallManager().getSubcomponent(this ,
604: DATA_DIR + name, callData);
605: } catch (ComponentNotFoundException e) {
606: return null;
607: } catch (ComponentPathException e) {
608: return null;
609: }
610: }
611:
612: protected String getDataAsString(String name, CallData callData) {
613: try {
614: Component component = ctx.getCallManager().getSubcomponent(
615: this , DATA_DIR + name, callData);
616: return renderComponent(component, callData);
617: } catch (ComponentNotFoundException e) {
618: return null;
619: } catch (ComponentPathException e) {
620: return null;
621: }
622: }
623:
624: protected Number getDataAsNumber(String name, CallData callData) {
625: try {
626: String value = getDataAsString(name, callData);
627:
628: if (value == null) {
629: return null;
630: }
631: return NumberFormat.getNumberInstance().parse(value);
632: } catch (ParseException e) {
633: return null;
634: }
635: }
636:
637: protected Boolean getDataAsBoolean(String name, CallData callData) {
638: String value = getDataAsString(name, callData);
639:
640: if (value == null) {
641: return null;
642: }
643: return Boolean.valueOf(value);
644: }
645:
646: protected void clearRequestStorageDomain(String storageKey,
647: CallData callData) {
648: callData.clearRequestStorageDomain(storageKey);
649: }
650:
651: protected void clearSessionStorageDomain(String storageKey,
652: CallData callData) {
653: callData.clearSessionStorageDomain(storageKey);
654: }
655:
656: }
|