1 package org.apache.maven.struts;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import java.io.IOException;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.maven.j2ee.WarClassLoader;
28 import org.apache.maven.j2ee.WarValidator;
29 import org.apache.regexp.RE;
30 import org.apache.regexp.RESyntaxException;
31
32 /**
33 * A class that validates a Struts 1.0 War File. Specific validations performed
34 * are:
35 *
36 * <ol>
37 * <li>
38 * Must pass validation as a 'standard' war file
39 * </li>
40 * <li>
41 * File has a struts configuration file
42 * </li>
43 * <li>
44 * <form-bean>s must have a valid <code>type</code> and
45 * <code>className</code> that exist in the war
46 * </li>
47 * <li>
48 * <action>s must have a valid <code>type</code>, <code>className</code>
49 * that exist in the war
50 * </li>
51 * <li>
52 * <action> <code>name</code>s must refer to a <form-bean> in the
53 * struts configuraion
54 * </li>
55 * <li>
56 * <action> <code>scope</code> must be either <code>request</code> or
57 * <code>session</code>
58 * </li>
59 * <li>
60 * <action> <code>unknown</code> and <code>validate</code> must be
61 * <code>true</code> or <code>false</code>
62 * </li>
63 * <li>
64 * <global-forwards> <code>type</code> must be a class in the war
65 * </li>
66 * <li>
67 * <forward> <code>redirect</code> must be <code>true</code> or
68 * <code>false</code>
69 * </li>
70 * <li>
71 * <forward> <code>path</code> must refer to either a file in the war
72 * (e.g. a jsp), or an action defined in the struts configuration
73 * </li>
74 * </ol>
75 *
76 *
77 * @version $Id: Struts10WarValidator.java 170200 2005-05-15 06:24:19Z brett $
78 * @author dion
79 */
80 public class Struts10WarValidator extends WarValidator
81 {
82
83
84 /**
85 * the location within the war of the Struts configuration file. By default
86 * this takes the value of the {@link Struts10WarFile#DEFAULT_CONFIG
87 * default config}
88 */
89 private String config = Struts10WarFile.DEFAULT_CONFIG;
90
91
92
93 /**
94 * Creates a new instance of Struts10WarValidator
95 */
96 public Struts10WarValidator()
97 {
98 }
99
100
101
102 /**
103 * Setter for config location within the war (no leading slash)
104 *
105 * @param config New value of property config.
106 */
107 public void setConfig(String config)
108 {
109 this.config = config;
110 }
111
112 /**
113 * Getter for Struts config location within the war (no leading slash).
114 * e.g. <code>WEB-INF/struts-config.xml</code>
115 *
116 * @return Value of property config.
117 */
118 public String getConfig()
119 {
120 return config;
121 }
122
123 /**
124 * Validate struts specific war features here
125 */
126 protected void validateWarContents()
127 {
128 try
129 {
130 super.validateWarContents();
131
132 Struts10WarFile strutsWar = new Struts10WarFile(getWarFileName());
133 strutsWar.setConfig(getConfig());
134 validateStrutsConfig(strutsWar);
135 validateFormBeans(strutsWar);
136 validateActions(strutsWar);
137 validateForwards(strutsWar);
138 }
139 catch (IOException ioe)
140 {
141 ioe.printStackTrace();
142 error("Error reading struts war file");
143 }
144 }
145
146 /**
147 * validate a string that must contain a boolean value or be null
148 *
149 * @param value the string to be validated
150 *
151 * @return true if the provided string is true, false or null
152 */
153 private boolean isBoolean(String value)
154 {
155 return (value == null) || value.equals("true") || value.equals("false");
156 }
157
158 /**
159 * validate the provided action object
160 *
161 * @param action the action to be validated
162 * @param formBeans the form beans for the war that the actions came from
163 * @param loader a {@link ClassLoader} to verify classes are from the war
164 */
165 private void validateAction(Action action, Map formBeans,
166 ClassLoader loader)
167 {
168 info("validating action for path: '" + action.getPath() + "'");
169
170
171 if (action.getClassName() != null)
172 {
173 validateClass(action.getClassName(), loader);
174 }
175
176
177 if ((action.getName() != null)
178 && (formBeans.get(action.getName()) == null))
179 {
180 error("action refers to a non-existent form bean: '"
181 + action.getName() + "'");
182 }
183
184
185 if (!action.getPath().startsWith("/"))
186 {
187 error("action path (" + action.getPath() + ") doesn't start "
188 + "with a '/'");
189 }
190
191
192 if ((action.getScope() != null)
193 && !(action.getScope().equals("request")
194 || action.getScope().equals("session")))
195 {
196 error("scope (" + action.getScope() + ") is not 'request' or "
197 + "'session'");
198 }
199
200
201 if (action.getType() != null)
202 {
203 validateClass(action.getType(), loader);
204 }
205
206
207 if (!isBoolean(action.getUnknown()))
208 {
209 error("unknown attribute is not 'true' or 'false'");
210 }
211
212
213 if (!isBoolean(action.getValidate()))
214 {
215 error("validate attribute is not 'true' or 'false'");
216 }
217 }
218
219 /**
220 * validations for the actions in the config
221 *
222 * @param war the struts web app being validated
223 *
224 * @throws IOException when there are problems reading the war
225 */
226 private void validateActions(Struts10WarFile war) throws IOException
227 {
228 info("validating Struts Actions");
229
230 List actions = war.getActions();
231 Action action = null;
232 ClassLoader loader = new WarClassLoader(war,
233 getClass().getClassLoader());
234 List formBeans = war.getFormBeans();
235 Map formBeansByName = new HashMap();
236
237
238 FormBean formBean = null;
239
240 for (int index = 0; index < formBeans.size(); index++)
241 {
242 formBean = (FormBean) formBeans.get(index);
243 formBeansByName.put(formBean.getName(), formBean);
244 }
245
246
247 for (int index = 0; index < actions.size(); index++)
248 {
249 action = (Action) actions.get(index);
250
251 if (CollectionUtils.cardinality(action, actions) > 1)
252 {
253 error("action is a duplicate (by path)");
254 }
255
256 validateAction(action, formBeansByName, loader);
257 }
258 }
259
260 /**
261 * validations for the form beans in the config
262 *
263 * @param strutsWar - the struts web app being validated
264 *
265 * @throws IOException when there are problems reading the war
266 */
267 private void validateFormBeans(Struts10WarFile strutsWar) throws IOException
268 {
269 info("validating Struts Form Beans");
270
271 List formBeans = strutsWar.getFormBeans();
272 FormBean bean = null;
273 ClassLoader loader = new WarClassLoader(strutsWar,
274 getClass().getClassLoader());
275
276 if (strutsWar.getFormBeansType() != null)
277 {
278 validateClass(strutsWar.getFormBeansType(), loader);
279 }
280
281 for (int index = 0; index < formBeans.size(); index++)
282 {
283 bean = (FormBean) formBeans.get(index);
284
285 if (CollectionUtils.cardinality(bean, formBeans) > 1)
286 {
287 error("form bean is a duplicate (by name)");
288 }
289
290 info("validating form bean: '" + bean.getName() + "', class: '"
291 + bean.getType() + "'");
292 validateClass(bean.getType(), loader);
293
294 if (bean.getClassName() != null)
295 {
296 validateClass(bean.getClassName(), loader);
297 }
298 }
299 }
300
301 /**
302 * Validate a single global forward.
303 *
304 * @param war the war file the forward is from
305 * @param forward the forward to be validated
306 * @param loader a class loader for validating classes are in the war
307 *
308 * @throws IOException when there are issues reading the war
309 * @throws IllegalStateException If the regular expression used to check
310 * that the forward matches the action servlet is malformed
311 */
312 private void validateForward(Struts10WarFile war, Forward forward,
313 ClassLoader loader) throws IOException
314 {
315
316 if (forward.getClassName() != null)
317 {
318 validateClass(forward.getClassName(), loader);
319 }
320
321
322 if (StringUtils.isEmpty(forward.getName()))
323 {
324 error("name attribute is required");
325 }
326
327
328 String path = forward.getPath();
329 int queryStringStart = path.indexOf("?");
330
331 if (queryStringStart != -1)
332 {
333 path = path.substring(0, queryStringStart);
334 }
335
336 if (!war.hasFile(path))
337 {
338 try
339 {
340
341
342
343 String pattern = war.getActionServletPattern();
344
345
346
347 pattern = StringUtils.replace(pattern, ".", "\\.");
348
349
350
351 pattern = StringUtils.replace(pattern, "*", "(.*)");
352
353 RE regexp = new RE(pattern);
354
355 if (regexp.match(path))
356 {
357 String actionPath = regexp.getParen(1);
358 Action dummyAction = new Action();
359 dummyAction.setPath(actionPath);
360
361 if (!war.getActions().contains(dummyAction))
362 {
363 error("action path for forward (" + actionPath + ")"
364 + " not found");
365 }
366 }
367 else
368 {
369 error("No action or web resource found for '" + path + "'");
370 }
371 }
372 catch (RESyntaxException e)
373 {
374 throw new IllegalStateException(
375 "bad regular expression created"
376 + " from action servlet url-pattern in web.xml "
377 + e.getMessage());
378 }
379 }
380
381
382 if (!isBoolean(forward.getRedirect()))
383 {
384 error("redirect attribute is not 'true' or 'false'");
385 }
386 }
387
388 /**
389 * validate the global forwards in the configuration
390 *
391 * @param war the war file to be used for validating
392 *
393 * @throws IOException when there are problems reading the war
394 */
395 private void validateForwards(Struts10WarFile war) throws IOException
396 {
397 info("validating global forwards");
398
399 ClassLoader loader = new WarClassLoader(war,
400 getClass().getClassLoader());
401
402 if (war.getGlobalForwardsType() != null)
403 {
404 validateClass(war.getGlobalForwardsType(), loader);
405 }
406
407 List forwards = war.getForwards();
408 Forward forward = null;
409
410 for (int index = 0; index < forwards.size(); index++)
411 {
412 forward = (Forward) forwards.get(index);
413 info("validating forward '" + forward.getName() + "', path: '"
414 + forward.getPath() + "'");
415
416 if (CollectionUtils.cardinality(forward, forwards) > 1)
417 {
418 error("forward is a duplicate (by name)");
419 }
420
421 validateForward(war, forward, loader);
422 }
423 }
424
425 /**
426 * validations for the struts configuration file
427 *
428 * @param strutsWar - the struts web app being validated
429 */
430 private void validateStrutsConfig(Struts10WarFile strutsWar)
431 {
432 info("validating Struts Configuration");
433
434 if (strutsWar.getStrutsConfigEntry() == null)
435 {
436 error("Struts Configuration: '" + strutsWar.getConfig()
437 + "' not found in the war file");
438 }
439 }
440 }