View Javadoc
1   /*
2    * $Id$
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  package org.apache.struts.config;
22  
23  import org.apache.struts.Globals;
24  import org.apache.struts.action.ActionForm;
25  import org.apache.struts.action.ActionFormBean;
26  import org.apache.struts.action.ActionForward;
27  import org.apache.struts.action.ActionMapping;
28  import org.apache.struts.action.ActionMessages;
29  import org.apache.struts.upload.MultipartRequestWrapper;
30  import org.apache.struts.util.MessageResources;
31  import org.apache.struts.util.RequestUtils;
32  
33  import jakarta.servlet.ServletContext;
34  import jakarta.servlet.http.HttpServletRequest;
35  import jakarta.servlet.http.HttpServletResponse;
36  import jakarta.servlet.http.HttpSession;
37  
38  /**
39   * <p> NOTE: THIS CLASS IS UNDER ACTIVE DEVELOPMENT. THE CURRENT CODE IS
40   * WRITTEN FOR CLARITY NOT EFFICIENCY. NOT EVERY API FUNCTION HAS BEEN
41   * IMPLEMENTED YET. </p><p> A helper object to expose the Struts shared
42   * resources, which are be stored in the application, session, or request
43   * contexts, as appropriate. </p><p> An instance should be created for each
44   * request processed. The  methods which return resources from the request or
45   * session contexts are not thread-safe. </p><p> Provided for use by other
46   * servlets in the application so they can easily access the Struts shared
47   * resources. </p><p> The resources are stored under attributes in the
48   * application, session, or request contexts. </p><p> The ActionConfig methods
49   * simply return the resources from under the context and key used by the
50   * Struts ActionServlet when the resources are created. </p>
51   *
52   * @version $Rev$ $Date: 2005-05-14 02:09:06 -0400 (Sat, 14 May 2005)
53   *          $
54   * @since Struts 1.1
55   */
56  public class ConfigHelper implements ConfigHelperInterface {
57      // --------------------------------------------------------  Properites
58  
59      /**
60       * <p> The application associated with this instance. </p>
61       */
62      private ServletContext application = null;
63  
64      /**
65       * <p> The session associated with this instance. </p>
66       */
67      private HttpSession session = null;
68  
69      /**
70       * <p> The request associated with this instance. </p>
71       */
72      private HttpServletRequest request = null;
73  
74      /**
75       * <p> The response associated with this instance. </p>
76       */
77      private HttpServletResponse response = null;
78  
79      /**
80       * <p> The forward associated with this instance. </p>
81       */
82      private ActionForward forward = null;
83  
84      public ConfigHelper() {
85          super();
86      }
87  
88      public ConfigHelper(ServletContext application, HttpServletRequest request,
89          HttpServletResponse response) {
90          super();
91          this.setResources(application, request, response);
92      }
93  
94      /**
95       * <p> Set the application associated with this instance.
96       * [servlet.getServletContext()] </p>
97       */
98      public void setApplication(ServletContext application) {
99          this.application = application;
100     }
101 
102     /**
103      * <p> Set the session associated with this instance. </p>
104      */
105     public void setSession(HttpSession session) {
106         this.session = session;
107     }
108 
109     /**
110      * <p> Set the request associated with this object. Session object is also
111      * set or cleared. </p>
112      */
113     public void setRequest(HttpServletRequest request) {
114         this.request = request;
115 
116         if (this.request == null) {
117             setSession(null);
118         } else {
119             setSession(this.request.getSession());
120         }
121     }
122 
123     /**
124      * <p> Set the response associated with this isntance. Session object is
125      * also set or cleared. </p>
126      */
127     public void setResponse(HttpServletResponse response) {
128         this.response = response;
129     }
130 
131     /**
132      * Set the forward associated with this instance.
133      */
134     public void setForward(ActionForward forward) {
135         this.forward = forward;
136     }
137 
138     /**
139      * <p> Set the application and request for this object instance. The
140      * ServletContext can be set by any servlet in the application. The
141      * request should be the instant request. Most of the other methods
142      * retrieve their own objects by reference to the application, request, or
143      * session attributes. Do not call other methods without setting these
144      * first! This is also called by the convenience constructor. </p>
145      *
146      * @param application - The associated ServletContext.
147      * @param request     - The associated HTTP request.
148      * @param response    - The associated HTTP response.
149      */
150     public void setResources(ServletContext application,
151         HttpServletRequest request, HttpServletResponse response) {
152         setApplication(application);
153         setRequest(request);
154         setResponse(response);
155     }
156 
157     // ------------------------------------------------ Application Context
158     public ActionMessages getActionMessages() {
159         if (this.application == null) {
160             return null;
161         }
162 
163         return (ActionMessages) this.application.getAttribute(Globals.MESSAGE_KEY);
164     }
165 
166     /**
167      * <p> The application resources for this application. </p>
168      */
169     public MessageResources getMessageResources() {
170         if (this.application == null) {
171             return null;
172         }
173 
174         return (MessageResources) this.application.getAttribute(Globals.MESSAGES_KEY);
175     }
176 
177     /**
178      * <p> The path-mapped pattern (<code>/action/*</code>) or extension
179      * mapped pattern ((<code>*.do</code>) used to determine our Action URIs
180      * in this application. </p>
181      */
182     public String getServletMapping() {
183         if (this.application == null) {
184             return null;
185         }
186 
187         return (String) this.application.getAttribute(Globals.SERVLET_KEY);
188     }
189 
190     // ---------------------------------------------------- Session Context
191 
192     /**
193      * <p> The transaction token stored in this session, if it is used. </p>
194      */
195     public String getToken() {
196         if (this.session == null) {
197             return null;
198         }
199 
200         return (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);
201     }
202 
203     // ---------------------------------------------------- Request Context
204 
205     /**
206      * <p> The runtime JspException that may be been thrown by a Struts tag
207      * extension, or compatible presentation extension, and placed in the
208      * request. </p>
209      */
210     public Throwable getException() {
211         if (this.request == null) {
212             return null;
213         }
214 
215         return (Throwable) this.request.getAttribute(Globals.EXCEPTION_KEY);
216     }
217 
218     /**
219      * <p> The multipart object for this request. </p>
220      */
221     public MultipartRequestWrapper getMultipartRequestWrapper() {
222         if (this.request == null) {
223             return null;
224         }
225 
226         return (MultipartRequestWrapper) this.request.getAttribute(Globals.MULTIPART_KEY);
227     }
228 
229     /**
230      * <p> The <code>org.apache.struts.ActionMapping</code> instance for this
231      * request. </p>
232      */
233     public ActionMapping getMapping() {
234         if (this.request == null) {
235             return null;
236         }
237 
238         return (ActionMapping) this.request.getAttribute(Globals.MAPPING_KEY);
239     }
240 
241     // ---------------------------------------------------- Utility Methods
242 
243     /**
244      * <p> Return true if a message string for the specified message key is
245      * present for the user's Locale. </p>
246      *
247      * @param key Message key
248      */
249     public boolean isMessage(String key) {
250         // Look up the requested MessageResources
251         MessageResources resources = getMessageResources();
252 
253         if (resources == null) {
254             return false;
255         }
256 
257         // Return the requested message presence indicator
258         return resources.isPresent(RequestUtils.getUserLocale(request, null),
259             key);
260     }
261 
262     /*
263      * <p>
264      * Retrieve and return the <code>ActionForm</code> bean associated with
265      * this mapping, creating and stashing one if necessary.  If there is no
266      * form bean associated with this mapping, return <code>null</code>.
267      * </p>
268      */
269     public ActionForm getActionForm() {
270         // Is there a mapping associated with this request?
271         ActionMapping mapping = getMapping();
272 
273         if (mapping == null) {
274             return (null);
275         }
276 
277         // Is there a form bean associated with this mapping?
278         String attribute = mapping.getAttribute();
279 
280         if (attribute == null) {
281             return (null);
282         }
283 
284         // Look up the existing form bean, if any
285         ActionForm instance;
286 
287         if ("request".equals(mapping.getScope())) {
288             instance = (ActionForm) this.request.getAttribute(attribute);
289         } else {
290             instance = (ActionForm) this.session.getAttribute(attribute);
291         }
292 
293         return instance;
294     }
295 
296     /**
297      * <p> Return the form bean definition associated with the specified
298      * logical name, if any; otherwise return <code>null</code>. </p>
299      *
300      * @param name Logical name of the requested form bean definition
301      */
302     public ActionFormBean getFormBean(String name) {
303         return null;
304     }
305 
306     /**
307      * <p> Return the forwarding associated with the specified logical name,
308      * if any; otherwise return <code>null</code>. </p>
309      *
310      * @param name Logical name of the requested forwarding
311      */
312     public ActionForward getActionForward(String name) {
313         return null;
314     }
315 
316     /**
317      * <p> Return the mapping associated with the specified request path, if
318      * any; otherwise return <code>null</code>. </p>
319      *
320      * @param path Request path for which a mapping is requested
321      */
322     public ActionMapping getActionMapping(String path) {
323         return null;
324     }
325 
326     /**
327      * <p> Return the form action converted into an action mapping path.  The
328      * value of the <code>action</code> property is manipulated as follows in
329      * computing the name of the requested mapping:</p>
330      *
331      * <ul>
332      *
333      * <li>Any filename extension is removed (on the theory that extension
334      * mapping is being used to select the controller servlet).</li>
335      *
336      * <li>If the resulting value does not start with a slash, then a slash is
337      * prepended.</li>
338      *
339      * </ul>
340      */
341     public String getActionMappingName(String action) {
342         String value = action;
343         int question = action.indexOf("?");
344 
345         if (question >= 0) {
346             value = value.substring(0, question);
347         }
348 
349         int slash = value.lastIndexOf("/");
350         int period = value.lastIndexOf(".");
351 
352         if ((period >= 0) && (period > slash)) {
353             value = value.substring(0, period);
354         }
355 
356         if (value.startsWith("/")) {
357             return (value);
358         } else {
359             return ("/" + value);
360         }
361     }
362 
363     /**
364      * <p> Return the form action converted into a server-relative URL. </p>
365      */
366     public String getActionMappingURL(String action) {
367         StringBuilder value = new StringBuilder(this.request.getContextPath());
368 
369         // Use our servlet mapping, if one is specified
370         String servletMapping = getServletMapping();
371 
372         if (servletMapping != null) {
373             String queryString = null;
374             int question = action.indexOf("?");
375 
376             if (question >= 0) {
377                 queryString = action.substring(question);
378             }
379 
380             String actionMapping = getActionMappingName(action);
381 
382             if (servletMapping.startsWith("*.")) {
383                 value.append(actionMapping);
384                 value.append(servletMapping.substring(1));
385             } else if (servletMapping.endsWith("/*")) {
386                 value.append(servletMapping.substring(0,
387                         servletMapping.length() - 2));
388                 value.append(actionMapping);
389             }
390 
391             if (queryString != null) {
392                 value.append(queryString);
393             }
394         }
395         // Otherwise, assume extension mapping is in use and extension is
396         // already included in the action property
397         else {
398             if (!action.startsWith("/")) {
399                 value.append("/");
400             }
401 
402             value.append(action);
403         }
404 
405         // Return the completed value
406         return (value.toString());
407     }
408 
409     /**
410      * <p> Return the url encoded to maintain the user session, if any. </p>
411      */
412     public String getEncodeURL(String url) {
413         if ((session != null) && (response != null)) {
414             boolean redirect = false;
415 
416             if (forward != null) {
417                 redirect = forward.getRedirect();
418             }
419 
420             if (redirect) {
421                 return response.encodeRedirectURL(url);
422             } else {
423                 return response.encodeURL(url);
424             }
425         } else {
426             return (url);
427         }
428     }
429 
430     // ------------------------------------------------ Presentation API
431 
432     /**
433      * <p> Renders the reference for a HTML &lt;base&gt; element </p>
434      */
435     public String getOrigRef() {
436         // HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
437         if (request == null) {
438             return null;
439         }
440 
441         StringBuilder result =
442             RequestUtils.requestToServerUriStringBuilder(request);
443 
444         return result.toString();
445     }
446 
447     /**
448      * <p> Renders the reference for a HTML <base> element. </p>
449      */
450     public String getBaseRef() {
451         if (request == null) {
452             return null;
453         }
454 
455         StringBuilder result = RequestUtils.requestToServerStringBuilder(request);
456         String path;
457 
458         if (forward == null) {
459             path = request.getRequestURI();
460         } else {
461             path = request.getContextPath() + forward.getPath();
462         }
463 
464         result.append(path);
465 
466         return result.toString();
467     }
468 
469     /**
470      * <p> Return the path for the specified forward, otherwise return
471      * <code>null</code>. </p>
472      *
473      * @param name Name given to local or global forward.
474      */
475     public String getLink(String name) {
476         ActionForward forward = getActionForward(name);
477 
478         if (forward == null) {
479             return null;
480         }
481 
482         StringBuilder path = new StringBuilder(this.request.getContextPath());
483 
484         path.append(forward.getPath());
485 
486         // :TODO: What about runtime parameters?
487         return getEncodeURL(path.toString());
488     }
489 
490     /**
491      * <p> Return the localized message for the specified key, otherwise
492      * return <code>null</code>. </p>
493      *
494      * @param key Message key
495      */
496     public String getMessage(String key) {
497         MessageResources resources = getMessageResources();
498 
499         if (resources == null) {
500             return null;
501         }
502 
503         return resources.getMessage(RequestUtils.getUserLocale(request, null),
504             key);
505     }
506 
507     /**
508      * <p> Look up and return a message string, based on the specified
509      * parameters. </p>
510      *
511      * @param key  Message key to be looked up and returned
512      * @param args Replacement parameters for this message
513      */
514     public String getMessage(String key, Object[] args) {
515         MessageResources resources = getMessageResources();
516 
517         if (resources == null) {
518             return null;
519         }
520 
521         // Return the requested message
522         if (args == null) {
523             return resources.getMessage(RequestUtils.getUserLocale(request, null),
524                 key);
525         } else {
526             return resources.getMessage(RequestUtils.getUserLocale(request, null),
527                 key, args);
528         }
529     }
530 
531     /**
532      * <p> Return the URL for the specified ActionMapping, otherwise return
533      * <code>null</code>. </p>
534      *
535      * @param path Name given to local or global forward.
536      */
537     public String getAction(String path) {
538         return getEncodeURL(getActionMappingURL(path));
539     }
540 
541     // --------------------------------------------- Presentation Wrappers
542 
543     /**
544      * <p> Wrapper for getLink(String) </p>
545      *
546      * @param name Name given to local or global forward.
547      */
548     public String link(String name) {
549         return getLink(name);
550     }
551 
552     /**
553      * <p> Wrapper for getMessage(String) </p>
554      *
555      * @param key Message key
556      */
557     public String message(String key) {
558         return getMessage(key);
559     }
560 
561     /**
562      * <p> Wrapper for getMessage(String,Object[]) </p>
563      *
564      * @param key  Message key to be looked up and returned
565      * @param args Replacement parameters for this message
566      */
567     public String message(String key, Object[] args) {
568         return getMessage(key, args);
569     }
570 
571     /**
572      * <p> Wrapper for getAction(String) </p>
573      *
574      * @param path Name given to local or global forward.
575      */
576     public String action(String path) {
577         return getAction(path);
578     }
579 }