View Javadoc

1   package org.apache.velocity.tools.struts;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Locale;
23  import java.util.Iterator;
24  
25  import javax.servlet.ServletContext;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpSession;
28  
29  import org.apache.struts.Globals;
30  import org.apache.struts.action.ActionForm;
31  import org.apache.struts.action.ActionMessage;
32  import org.apache.struts.action.ActionMessages;
33  import org.apache.struts.config.ModuleConfig;
34  import org.apache.struts.config.ForwardConfig;
35  import org.apache.struts.config.ActionConfig;
36  import org.apache.struts.util.MessageResources;
37  import org.apache.struts.util.RequestUtils;
38  import org.apache.struts.taglib.TagUtils;
39  import org.apache.struts.util.ModuleUtils;
40  
41  /**
42   * <p>A utility class to expose the Struts shared
43   * resources. All methods are static.</p>
44   *
45   * <p>This class is provided for use by Velocity view tools
46   * that need access to Struts resources. By having all Struts-
47   * specific code in this utility class, maintenance is simplified
48   * and reuse fostered.</p>
49   *
50   * <p>It is the aim, that sooner or later the functionality in
51   * this class is integrated into Struts itself.  See
52   * <a href="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16814">Bug #16814</a>
53   * for more on that.</p>
54   *
55   * @author <a href="mailto:marinoj@centrum.is">Marino A. Jonsson</a>
56   * @author Nathan Bubna
57   * @author <a href="mailto:sidler@teamup.com">Gabe Sidler</a>
58   * based on code by <a href="mailto:ted@husted.org">Ted Husted</a>
59   *
60   * @version $Id: StrutsUtils.java 670093 2008-06-20 23:10:11Z nbubna $
61   */
62  public class StrutsUtils
63  {
64      public static final StrutsUtils INSTANCE = new StrutsUtils();
65  
66      private StrutsUtils() {}
67  
68      public StrutsUtils getInstance()
69      {
70          return INSTANCE;
71      }
72  
73      /****************** Struts ServletContext Resources ****************/
74  
75      /**
76       * Returns the message resources for this application or <code>null</code>
77       * if not found.
78       *
79       * @param app the servlet context
80       * @since VelocityTools 1.1
81       */
82      public static MessageResources getMessageResources(HttpServletRequest request,
83                                                         ServletContext app)
84      {
85          return getMessageResources(request, app, null);
86      }
87  
88  
89      /**
90       * Returns the message resources with the specified bundle name for this application
91       * or <code>null</code> if not found.
92       *
93       * @param app the servlet context
94       * @param bundle The bundle name to look for.  If this is <code>null</code>, the
95       *               default bundle name is used.
96       * @since VelocityTools 1.1
97       */
98      public static MessageResources getMessageResources(HttpServletRequest request,
99                                                         ServletContext app,
100                                                        String bundle)
101     {
102         /* Identify the current module */
103         ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request, app);
104 
105         if (bundle == null) {
106             bundle = Globals.MESSAGES_KEY;
107         }
108 
109         // First check request scope
110         MessageResources resources =
111             (MessageResources)request.getAttribute(bundle + moduleConfig.getPrefix());
112 
113         if (resources == null) {
114             resources = (MessageResources) app.getAttribute(bundle + moduleConfig.getPrefix());
115         }
116         return resources;
117     }
118 
119 
120     /**
121      * Select the module to which the specified request belongs, and
122      * add return the corresponding ModuleConfig.
123      *
124      * @param urlPath The requested URL
125      * @param app The ServletContext for this web application
126      * @return The ModuleConfig for the given URL path
127      * @since VelocityTools 1.1
128      */
129     public static ModuleConfig selectModule(String urlPath,
130                                             ServletContext app)
131     {
132         /* Match against the list of sub-application prefixes */
133         String prefix = ModuleUtils.getInstance().getModuleName(urlPath, app);
134 
135         /* Expose the resources for this sub-application */
136         ModuleConfig config = (ModuleConfig)
137             app.getAttribute(Globals.MODULE_KEY + prefix);
138 
139         return config;
140     }
141 
142 
143     /********************** Struts Session Resources ******************/
144 
145     /**
146      * Returns the <code>java.util.Locale</code> for the user. If a
147      * locale object is not found in the user's session, the system
148      * default locale is returned.
149      *
150      * @param request the servlet request
151      * @param session the HTTP session
152      */
153     public static Locale getLocale(HttpServletRequest request,
154                                    HttpSession session)
155     {
156         Locale locale = null;
157 
158         if (session != null)
159         {
160             locale = (Locale)session.getAttribute(Globals.LOCALE_KEY);
161         }
162         if (locale == null)
163         {
164             locale = request.getLocale();
165         }
166         return locale;
167     }
168 
169 
170     /**
171      * Returns the transaction token stored in this session or
172      * <code>null</code> if not used.
173      *
174      * @param session the HTTP session
175      */
176     public static String getToken(HttpSession session)
177     {
178         if (session == null)
179         {
180             return null;
181         }
182         return (String)session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
183     }
184 
185 
186     /*********************** Struts Request Resources ****************/
187 
188     /**
189      * Returns the Struts errors for this request or <code>null</code>
190      * if none exist. Since VelocityTools 1.2, this will also check
191      * the session (if there is one) for errors if there are no errors
192      * in the request.
193      *
194      * @param request the servlet request
195      * @since VelocityTools 1.1
196      */
197     public static ActionMessages getErrors(HttpServletRequest request)
198     {
199         ActionMessages errors = (ActionMessages)request.getAttribute(Globals.ERROR_KEY);
200         if (errors == null || errors.isEmpty())
201         {
202             // then check the session
203             HttpSession session = request.getSession(false);
204             if (session != null)
205             {
206                 errors = (ActionMessages)session.getAttribute(Globals.ERROR_KEY);
207             }
208         }
209         return errors;
210     }
211 
212     /**
213      * Returns the Struts messages for this request or <code>null</code>
214      * if none exist.  Since VelocityTools 1.2, this will also check
215      * the session (if there is one) for messages if there are no messages
216      * in the request.
217      *
218      * @param request the servlet request
219      * @since VelocityTools 1.1
220      */
221     public static ActionMessages getMessages(HttpServletRequest request)
222     {
223         ActionMessages messages = (ActionMessages)request.getAttribute(Globals.MESSAGE_KEY);
224         if (messages == null || messages.isEmpty())
225         {
226             // then check the session
227             HttpSession session = request.getSession(false);
228             if (session != null)
229             {
230                 messages = (ActionMessages)session.getAttribute(Globals.MESSAGE_KEY);
231             }
232         }
233         return messages;
234     }
235 
236     /**
237      * Returns the <code>ActionForm</code> bean associated with
238      * this request of <code>null</code> if none exists.
239      *
240      * @param request the servlet request
241      * @param session the HTTP session
242      */
243     public static ActionForm getActionForm(HttpServletRequest request,
244                                            HttpSession session)
245     {
246         /* Is there a mapping associated with this request? */
247         ActionConfig mapping =
248             (ActionConfig)request.getAttribute(Globals.MAPPING_KEY);
249         if (mapping == null)
250         {
251             return null;
252         }
253 
254         /* Is there a form bean associated with this mapping? */
255         String attribute = mapping.getAttribute();
256         if (attribute == null)
257         {
258             return null;
259         }
260 
261         /* Look up the existing form bean */
262         if ("request".equals(mapping.getScope()))
263         {
264             return (ActionForm)request.getAttribute(attribute);
265         }
266         if (session != null)
267         {
268             return (ActionForm)session.getAttribute(attribute);
269         }
270         return null;
271     }
272 
273     /**
274      * Returns the ActionForm name associated with
275      * this request of <code>null</code> if none exists.
276      *
277      * @param request the servlet request
278      * @param session the HTTP session
279      */
280     public static String getActionFormName(HttpServletRequest request,
281                                            HttpSession session)
282     {
283         /* Is there a mapping associated with this request? */
284         ActionConfig mapping =
285             (ActionConfig)request.getAttribute(Globals.MAPPING_KEY);
286         if (mapping == null)
287         {
288             return null;
289         }
290 
291         return mapping.getAttribute();
292     }
293 
294 
295 
296     /*************************** Utilities *************************/
297 
298     /**
299      * Return the form action converted into an action mapping path.  The
300      * value of the <code>action</code> property is manipulated as follows in
301      * computing the name of the requested mapping:
302      * <ul>
303      * <li>Any filename extension is removed (on the theory that extension
304      *     mapping is being used to select the controller servlet).</li>
305      * <li>If the resulting value does not start with a slash, then a
306      *     slash is prepended.</li>
307      * </ul>
308      */
309     public static String getActionMappingName(String action) {
310 
311         String value = action;
312         int question = action.indexOf('?');
313         if (question >= 0) {
314             value = value.substring(0, question);
315         }
316 
317         int slash = value.lastIndexOf('/');
318         int period = value.lastIndexOf('.');
319         if ((period >= 0) && (period > slash)) {
320             value = value.substring(0, period);
321         }
322 
323         return value.startsWith("/") ? value : ("/" + value);
324     }
325 
326     /**
327      * Returns the form action converted into a server-relative URI
328      * reference.
329      *
330      * @param application the servlet context
331      * @param request the servlet request
332      * @param action the name of an action as per struts-config.xml
333      */
334     public static String getActionMappingURL(ServletContext application,
335                                              HttpServletRequest request,
336                                              String action)
337     {
338         StringBuilder value = new StringBuilder(request.getContextPath());
339         ModuleConfig config =
340             (ModuleConfig)request.getAttribute(Globals.MODULE_KEY);
341         if (config != null)
342         {
343             value.append(config.getPrefix());
344         }
345 
346         /* Use our servlet mapping, if one is specified */
347         String servletMapping =
348             (String)application.getAttribute(Globals.SERVLET_KEY);
349 
350         if (servletMapping != null)
351         {
352             String queryString = null;
353             int question = action.indexOf('?');
354 
355             if (question >= 0)
356             {
357                 queryString = action.substring(question);
358             }
359 
360             String actionMapping = TagUtils.getInstance().getActionMappingName(action);
361 
362             if (servletMapping.startsWith("*."))
363             {
364                 value.append(actionMapping);
365                 value.append(servletMapping.substring(1));
366             }
367             else if (servletMapping.endsWith("/*"))
368             {
369                 value.append(servletMapping.substring
370                              (0, servletMapping.length() - 2));
371                 value.append(actionMapping);
372             }
373 
374             if (queryString != null)
375             {
376                 value.append(queryString);
377             }
378         }
379         else
380         {
381             /* Otherwise, assume extension mapping is in use and extension is
382              * already included in the action property */
383             if (!action.startsWith("/"))
384             {
385                 value.append("/");
386             }
387             value.append(action);
388         }
389 
390         /* Return the completed value */
391         return value.toString();
392     }
393 
394 
395     /**
396      * Returns the action forward name converted into a server-relative URI
397      * reference.
398      *
399      * @param app the servlet context
400      * @param request the servlet request
401      * @param forward the name of a forward as per struts-config.xml
402      */
403     public static String getForwardURL(HttpServletRequest request,
404                                        ServletContext app,
405                                        String forward)
406     {
407         ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request, app);
408         //TODO? beware of null module config if ActionServlet isn't init'ed?
409 
410         ActionConfig actionConfig =
411             (ActionConfig)request.getAttribute(Globals.MAPPING_KEY);
412 
413         // NOTE: ActionConfig.findForwardConfig only searches local forwards
414         ForwardConfig fc = null;
415         if(actionConfig != null)
416         {
417             fc = actionConfig.findForwardConfig(forward);
418         }
419 
420         // No ActionConfig forward?
421         // Find the ForwardConfig in the global-forwards.
422         if(fc == null)
423         {
424             fc = moduleConfig.findForwardConfig(forward);
425 
426             // ok, give up
427             if (fc == null)
428             {
429                 return null;
430             }
431         }
432 
433         StringBuilder url = new StringBuilder();
434         if (fc.getPath().startsWith("/"))
435         {
436             url.append(request.getContextPath());
437             url.append(RequestUtils.forwardURL(request, fc, moduleConfig));
438         }
439         else
440         {
441             url.append(fc.getPath());
442         }
443         return url.toString();
444     }
445 
446 
447     /**
448      * Returns a formatted error message. The error message is assembled from
449      * the following three pieces: First, value of message resource
450      * "errors.header" is prepended. Then, the list of error messages is
451      * rendered. Finally, the value of message resource "errors.footer"
452      * is appended.
453      *
454      * @param property the category of errors to markup and return
455      * @param request the servlet request
456      * @param session the HTTP session
457      * @param application the servlet context
458      *
459      * @return The formatted error message. If no error messages are queued,
460      * an empty string is returned.
461      */
462     public static String errorMarkup(String property,
463                                      HttpServletRequest request,
464                                      HttpSession session,
465                                      ServletContext application)
466     {
467         return errorMarkup(property, null, request, session, application);
468     }
469 
470 
471     /**
472      * Returns a formatted error message. The error message is assembled from
473      * the following three pieces: First, value of message resource
474      * "errors.header" is prepended. Then, the list of error messages is
475      * rendered. Finally, the value of message resource "errors.footer"
476      * is appended.
477      *
478      * @param property the category of errors to markup and return
479      * @param bundle the message resource bundle to use
480      * @param request the servlet request
481      * @param session the HTTP session
482      * @param application the servlet context
483      * @since VelocityTools 1.1
484      * @return The formatted error message. If no error messages are queued,
485      * an empty string is returned.
486      */
487     public static String errorMarkup(String property,
488                                      String bundle,
489                                      HttpServletRequest request,
490                                      HttpSession session,
491                                      ServletContext application)
492     {
493         ActionMessages errors = getErrors(request);
494         if (errors == null)
495         {
496             return "";
497         }
498 
499         /* fetch the error messages */
500         Iterator reports = null;
501         if (property == null)
502         {
503             reports = errors.get();
504         }
505         else
506         {
507             reports = errors.get(property);
508         }
509 
510         if (!reports.hasNext())
511         {
512             return "";
513         }
514 
515         /* Render the error messages appropriately if errors have been queued */
516         StringBuilder results = new StringBuilder();
517         String header = null;
518         String footer = null;
519         String prefix = null;
520         String suffix = null;
521         Locale locale = getLocale(request, session);
522 
523         MessageResources resources =
524             getMessageResources(request, application, bundle);
525         if (resources != null)
526         {
527             header = resources.getMessage(locale, "errors.header");
528             footer = resources.getMessage(locale, "errors.footer");
529             prefix = resources.getMessage(locale, "errors.prefix");
530             suffix = resources.getMessage(locale, "errors.suffix");
531         }
532         if (header == null)
533         {
534             header = "errors.header";
535         }
536         if (footer == null)
537         {
538             footer = "errors.footer";
539         }
540         /* prefix or suffix are optional, be quiet if they're missing */
541         if (prefix == null)
542         {
543             prefix = "";
544         }
545         if (suffix == null)
546         {
547             suffix = "";
548         }
549 
550         results.append(header);
551         results.append("\r\n");
552 
553         String message;
554         while (reports.hasNext())
555         {
556             message = null;
557             ActionMessage report = (ActionMessage)reports.next();
558             if (resources != null && report.isResource())
559             {
560                 message = resources.getMessage(locale,
561                                                report.getKey(),
562                                                report.getValues());
563             }
564 
565             results.append(prefix);
566 
567             if (message != null)
568             {
569                 results.append(message);
570             }
571             else
572             {
573                 results.append(report.getKey());
574             }
575 
576             results.append(suffix);
577             results.append("\r\n");
578         }
579 
580         results.append(footer);
581         results.append("\r\n");
582 
583         /* return result */
584         return results.toString();
585     }
586 
587 }