001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.pageflow.internal;
020:
021: import org.apache.struts.validator.ValidatorForm;
022: import org.apache.struts.validator.Resources;
023: import org.apache.struts.action.ActionErrors;
024: import org.apache.struts.action.ActionMapping;
025: import org.apache.struts.action.ActionMessage;
026: import org.apache.struts.util.MessageResources;
027: import org.apache.struts.Globals;
028: import org.apache.commons.validator.Validator;
029: import org.apache.commons.validator.ValidatorException;
030:
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.ServletContext;
033:
034: import org.apache.beehive.netui.util.logging.Logger;
035: import org.apache.beehive.netui.pageflow.config.PageFlowActionMapping;
036: import org.apache.beehive.netui.pageflow.Validatable;
037:
038: import java.lang.reflect.Method;
039: import java.lang.reflect.InvocationTargetException;
040: import java.util.Locale;
041: import java.util.Iterator;
042:
043: /**
044: * Base class for form beans associated with action methods in
045: * {@link org.apache.beehive.netui.pageflow.PageFlowController}s. Note that Page Flow actions
046: * may take form beans of any type.
047: */
048: public class BaseActionForm extends ValidatorForm {
049: private static final Logger _log = Logger
050: .getInstance(BaseActionForm.class);
051:
052: //
053: // This is used to allow us to run against Validator 1.0 or 1.1. The reflective Method is only used when running
054: // against Validator 1.0 (legacy).
055: //
056: private static Method _legacyInitValidatorMethod = null;
057:
058: static {
059: try {
060: _legacyInitValidatorMethod = Resources.class.getMethod(
061: "initValidator", new Class[] { String.class,
062: Object.class, ServletContext.class,
063: HttpServletRequest.class,
064: ActionErrors.class, int.class });
065: } catch (NoSuchMethodException e) {
066: // ignore -- we're in Validator 1.1 or later.
067: }
068: }
069:
070: public ActionErrors validate(ActionMapping mapping,
071: HttpServletRequest request) {
072: return validateBean(this , mapping.getAttribute(), mapping,
073: request);
074: }
075:
076: /**
077: * MessageResources that conglomerates a primary and backup MessageResources.
078: */
079: private static class MergedMessageResources extends
080: MessageResources {
081: private MessageResources _primary;
082: private MessageResources _backup;
083:
084: public MergedMessageResources(MessageResources primary,
085: MessageResources backup) {
086: super (primary.getFactory(), primary.getConfig(), primary
087: .getReturnNull());
088: _primary = primary;
089: _backup = backup;
090: }
091:
092: public String getMessage(Locale locale, String key) {
093: String message = _primary.getMessage(locale, key);
094: if (message == null)
095: message = _backup.getMessage(locale, key);
096: return message;
097: }
098: }
099:
100: /**
101: * Run all validation (declarative validation from annotations and the result of {@link org.apache.beehive.netui.pageflow.Validatable#validate}) on
102: * a given bean.
103: *
104: * @param bean the bean to validate.
105: * @param beanName the name of the bean, to be passed to Validator to look up declarative validation rules.
106: * @param mapping the current ActionMapping.
107: * @param request the current HttpServletRequest.
108: * @return an ActionErrors object containing errors that occurred during bean validation.
109: */
110: protected ActionErrors validateBean(Object bean, String beanName,
111: ActionMapping mapping, HttpServletRequest request) {
112: MessageResources messageResources = (MessageResources) request
113: .getAttribute(Globals.MESSAGES_KEY);
114: ExpressionAwareMessageResources.update(messageResources, bean);
115:
116: //
117: // See if this action uses a form that defines its own message resources. If so, use those, or combine them
118: // with the message resources from the current module.
119: //
120: if (mapping instanceof PageFlowActionMapping) {
121: PageFlowActionMapping pfam = (PageFlowActionMapping) mapping;
122: String bundle = pfam.getFormBeanMessageResourcesKey();
123:
124: if (bundle != null) {
125: MessageResources formBeanResources = (MessageResources) request
126: .getAttribute(bundle);
127: ExpressionAwareMessageResources.update(
128: formBeanResources, bean);
129:
130: if (formBeanResources != null) {
131: if (messageResources != null) {
132: formBeanResources = new MergedMessageResources(
133: messageResources, formBeanResources);
134: }
135:
136: request.setAttribute(Globals.MESSAGES_KEY,
137: formBeanResources);
138: messageResources = formBeanResources;
139: }
140: }
141: }
142:
143: ServletContext servletContext = getServlet()
144: .getServletContext();
145:
146: // If there's still no MessageResources for this request, create one that can evaluate expressions.
147: if (messageResources == null) {
148: messageResources = new ExpressionAwareMessageResources(
149: bean, request, servletContext);
150: request
151: .setAttribute(Globals.MESSAGES_KEY,
152: messageResources);
153: }
154:
155: ActionErrors errors = new ActionErrors();
156:
157: //
158: // If the ValidatorPlugIn was initialized for this module, run it.
159: //
160: if (Resources.getValidatorResources(servletContext, request) != null) {
161: try {
162: //
163: // Run validations associated with the bean.
164: //
165: Validator beanV = initValidator(beanName, bean,
166: servletContext, request, errors, page);
167: validatorResults = beanV.validate();
168:
169: //
170: // Run validations associated with the action.
171: //
172: Validator actionV = initValidator(mapping.getPath(),
173: bean, servletContext, request, errors, page);
174: validatorResults.merge(actionV.validate());
175: } catch (ValidatorException e) {
176: _log.error(e.getMessage(), e);
177: }
178: }
179:
180: //
181: // If this bean implements our Validatable interface, run its validate method.
182: //
183: if (bean instanceof Validatable) {
184: ((Validatable) bean).validate(mapping, request, errors);
185: }
186:
187: // Add any additional errors specified by a subclass.
188: ActionErrors additionalActionErrors = getAdditionalActionErrors(
189: mapping, request);
190: if (additionalActionErrors != null) {
191: mergeActionErrors(errors, additionalActionErrors);
192: }
193:
194: return errors;
195: }
196:
197: private static void mergeActionErrors(ActionErrors main,
198: ActionErrors toAdd) {
199: for (Iterator i = toAdd.properties(); i.hasNext();) {
200: String propertyName = (String) i.next();
201:
202: for (Iterator j = toAdd.get(propertyName); j.hasNext();) {
203: ActionMessage actionMessage = (ActionMessage) j.next();
204: boolean alreadyExists = false;
205:
206: for (Iterator k = main.get(propertyName); k.hasNext();) {
207: ActionMessage existingActionMessage = (ActionMessage) k
208: .next();
209:
210: if (existingActionMessage.getKey().equals(
211: actionMessage.getKey())) {
212: alreadyExists = true;
213: break;
214: }
215: }
216:
217: if (!alreadyExists) {
218: main.add(propertyName, actionMessage);
219: }
220: }
221: }
222: }
223:
224: /**
225: * Get an additional list of validation errors. This list will upplemented the errors from declarative
226: * validation (annotations) and invocation of {@link org.apache.beehive.netui.pageflow.Validatable#validate} if the {@link org.apache.beehive.netui.pageflow.Validatable} interface
227: * is implemented. The base implementation returns <code>null</code>, which signifies that there are no additional
228: * errors.
229: */
230: protected ActionErrors getAdditionalActionErrors(
231: ActionMapping mapping, HttpServletRequest request) {
232: return null;
233: }
234:
235: private static Validator initValidator(String beanName,
236: Object bean, ServletContext context,
237: HttpServletRequest request, ActionErrors errors, int page) {
238: if (_legacyInitValidatorMethod != null) {
239: try {
240: Object[] args = new Object[] { beanName, bean, context,
241: request, errors, new Integer(page) };
242: Validator validator = (Validator) _legacyInitValidatorMethod
243: .invoke(Resources.class, args);
244:
245: //
246: // The NetUI validator rules work on both 1.1 and 1.2. They take ActionMessages instead of ActionErrors.
247: //
248: validator.addResource(
249: "org.apache.struts.action.ActionMessages",
250: errors);
251: return validator;
252: } catch (IllegalAccessException e) {
253: assert false : e.getMessage();
254: throw new RuntimeException(e);
255: } catch (InvocationTargetException e) {
256: assert false : e.getMessage();
257: throw new RuntimeException(e);
258: }
259: } else {
260: return Resources.initValidator(beanName, bean, context,
261: request, errors, page);
262: }
263: }
264: }
|