View Javadoc

1   /*
2    * $Id: UrlHelper.java 651946 2008-04-27 13:41:38Z apetrelli $
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  package org.apache.struts2.views.util;
23  
24  import java.io.UnsupportedEncodingException;
25  import java.net.URLDecoder;
26  import java.net.URLEncoder;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Iterator;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  import org.apache.struts2.ServletActionContext;
38  import org.apache.struts2.StrutsConstants;
39  
40  import com.opensymphony.xwork2.ActionContext;
41  import com.opensymphony.xwork2.inject.Container;
42  import com.opensymphony.xwork2.util.TextParseUtil;
43  import com.opensymphony.xwork2.util.ValueStack;
44  import com.opensymphony.xwork2.util.logging.Logger;
45  import com.opensymphony.xwork2.util.logging.LoggerFactory;
46  
47  
48  /***
49   * UrlHelper
50   *
51   */
52  public class UrlHelper {
53      private static final Logger LOG = LoggerFactory.getLogger(UrlHelper.class);
54  
55      /***
56       * Default HTTP port (80).
57       */
58      private static final int DEFAULT_HTTP_PORT = 80;
59  
60      /***
61       * Default HTTPS port (443).
62       */
63      private static final int DEFAULT_HTTPS_PORT = 443;
64  
65      private static final String AMP = "&";
66  
67      public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params) {
68          return buildUrl(action, request, response, params, null, true, true);
69      }
70  
71      public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult) {
72          return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, false);
73      }
74  
75      public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) {
76      	return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true);
77      }
78      
79      public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) {
80          StringBuffer link = new StringBuffer();
81  
82          boolean changedScheme = false;
83          
84          // FIXME: temporary hack until class is made a properly injected bean
85          Container cont = ActionContext.getContext().getContainer();
86          int httpPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTP_PORT));
87          int httpsPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTPS_PORT));
88  
89          // only append scheme if it is different to the current scheme *OR*
90          // if we explicity want it to be appended by having forceAddSchemeHostAndPort = true
91          if (forceAddSchemeHostAndPort) {
92              String reqScheme = request.getScheme();
93              changedScheme = true;
94              link.append(scheme != null ? scheme : reqScheme);
95              link.append("://");
96              link.append(request.getServerName());
97  
98              if (scheme != null) {
99                  // If switching schemes, use the configured port for the particular scheme.
100                 if (!scheme.equals(reqScheme)) {
101                     if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT)) {
102                         link.append(":");
103                         link.append(scheme.equals("http") ? httpPort : httpsPort);
104                     }
105                 // Else use the port from the current request.
106                 } else {
107                     int reqPort = request.getServerPort();
108 
109                     if ((scheme.equals("http") && (reqPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && reqPort != DEFAULT_HTTPS_PORT)) {
110                         link.append(":");
111                         link.append(reqPort);
112                     }
113                 }
114             }
115         }
116         else if ((scheme != null) && !scheme.equals(request.getScheme())) {
117             changedScheme = true;
118             link.append(scheme);
119             link.append("://");
120             link.append(request.getServerName());
121 
122             if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT))
123             {
124                 link.append(":");
125                 link.append(scheme.equals("http") ? httpPort : httpsPort);
126             }
127         }
128 
129         if (action != null) {
130             // Check if context path needs to be added
131             // Add path to absolute links
132             if (action.startsWith("/") && includeContext) {
133                 String contextPath = request.getContextPath();
134                 if (!contextPath.equals("/")) {
135                     link.append(contextPath);
136                 }
137             } else if (changedScheme) {
138 
139                 // (Applicable to Servlet 2.4 containers)
140                 // If the request was forwarded, the attribute below will be set with the original URL
141                 String uri = (String) request.getAttribute("javax.servlet.forward.request_uri");
142 
143                 // If the attribute wasn't found, default to the value in the request object
144                 if (uri == null) {
145                     uri = request.getRequestURI();
146                 }
147 
148                 link.append(uri.substring(0, uri.lastIndexOf('/') + 1));
149             }
150 
151             // Add page
152             link.append(action);
153         } else {
154             // Go to "same page"
155             String requestURI = (String) request.getAttribute("struts.request_uri");
156 
157             // (Applicable to Servlet 2.4 containers)
158             // If the request was forwarded, the attribute below will be set with the original URL
159             if (requestURI == null) {
160                 requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri");
161             }
162 
163             // If neither request attributes were found, default to the value in the request object
164             if (requestURI == null) {
165                 requestURI = request.getRequestURI();
166             }
167 
168             link.append(requestURI);
169         }
170 
171         //if the action was not explicitly set grab the params from the request
172         if (escapeAmp) {
173             buildParametersString(params, link);
174         } else {
175             buildParametersString(params, link, "&");
176         } 
177 
178         String result = link.toString();
179         
180         while (result.indexOf("<script>") > 0){
181         	result = result.replaceAll("<script>", "script");
182         }        
183         try {
184             result = encodeResult ? response.encodeURL(result) : result;
185         } catch (Exception ex) {
186             // Could not encode the URL for some reason
187             // Use it unchanged
188             result = link.toString();
189         }
190 
191         return result;
192     }
193 
194     public static void buildParametersString(Map params, StringBuffer link) {
195         buildParametersString(params, link, AMP);
196     }
197 
198     public static void buildParametersString(Map params, StringBuffer link, String paramSeparator) {
199         if ((params != null) && (params.size() > 0)) {
200             if (link.toString().indexOf("?") == -1) {
201                 link.append("?");
202             } else {
203                 link.append(paramSeparator);
204             }
205 
206             // Set params
207             Iterator iter = params.entrySet().iterator();
208 
209 
210             while (iter.hasNext()) {
211                 Map.Entry entry = (Map.Entry) iter.next();
212                 String name = (String) entry.getKey();
213                 Object value = entry.getValue();
214 
215 
216                 if (value instanceof Iterable) {
217                     for (Iterator iterator = ((Iterable) value).iterator(); iterator
218                         .hasNext();) {
219                         Object paramValue = iterator.next();
220                         link.append(buildParameterSubstring(name, paramValue
221                             .toString()));
222 
223                         if (iterator.hasNext())
224                             link.append(paramSeparator);
225                     }
226                 } else if (value instanceof Object[]) {
227                     Object[] array = (Object[]) value;
228                     for (int i = 0; i < array.length; i++) {
229                         Object paramValue = array[i];
230                         link.append(buildParameterSubstring(name, paramValue
231                             .toString()));
232 
233                         if (i < array.length - 1)
234                             link.append(paramSeparator);
235                     }
236                 } else {
237                     link.append(buildParameterSubstring(name, value.toString()));
238                 }
239                 
240                 if (iter.hasNext())
241                     link.append(paramSeparator);
242             }
243         }
244     }
245 
246     
247     private static String buildParameterSubstring(String name, String value) {
248         StringBuilder builder = new StringBuilder();
249         builder.append(name);
250         builder.append('=');
251         builder.append(translateAndEncode(value));
252         
253         return builder.toString();
254     }
255     
256     /***
257      * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and
258      * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration.
259      *
260      * @param input
261      * @return the translated and encoded string
262      */
263     public static String translateAndEncode(String input) {
264         String translatedInput = translateVariable(input);
265         String encoding = getEncodingFromConfiguration();
266 
267         try {
268             return URLEncoder.encode(translatedInput, encoding);
269         } catch (UnsupportedEncodingException e) {
270             LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded");
271             return translatedInput;
272         }
273     }
274 
275     public static String translateAndDecode(String input) {
276         String translatedInput = translateVariable(input);
277         String encoding = getEncodingFromConfiguration();
278 
279         try {
280             return URLDecoder.decode(translatedInput, encoding);
281         } catch (UnsupportedEncodingException e) {
282             LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded");
283             return translatedInput;
284         }
285     }
286 
287     private static String translateVariable(String input) {
288         ValueStack valueStack = ServletActionContext.getContext().getValueStack();
289         String output = TextParseUtil.translateVariables(input, valueStack);
290         return output;
291     }
292 
293     private static String getEncodingFromConfiguration() {
294         final String encoding;
295         
296         // FIXME: temporary hack until class is made a properly injected bean
297         Container cont = ActionContext.getContext().getContainer();
298         String customEncoding = cont.getInstance(String.class, StrutsConstants.STRUTS_I18N_ENCODING);
299         
300         if (customEncoding != null) {
301             encoding = customEncoding;
302         } else {
303             encoding = "UTF-8";
304         }
305         return encoding;
306     }
307 
308     public static Map parseQueryString(String queryString) {
309         Map queryParams = new LinkedHashMap();
310         if (queryString != null) {
311             String[] params = queryString.split("&");
312             for (int a=0; a< params.length; a++) {
313                 if (params[a].trim().length() > 0) {
314                     String[] tmpParams = params[a].split("=");
315                     String paramName = null;
316                     String paramValue = "";
317                     if (tmpParams.length > 0) {
318                         paramName = tmpParams[0];
319                     }
320                     if (tmpParams.length > 1) {
321                         paramValue = tmpParams[1];
322                     }
323                     if (paramName != null) {
324                         String translatedParamValue = translateAndDecode(paramValue);
325 
326                         if(queryParams.containsKey(paramName)) {
327                             // WW-1619 append new param value to existing value(s)
328                             Object currentParam = queryParams.get(paramName);
329                             if(currentParam instanceof String) {
330                                 queryParams.put(paramName, new String[] {
331                                         (String) currentParam, translatedParamValue});
332                             } else {
333                                 String currentParamValues[] = (String[]) currentParam;
334                                 List paramList = new ArrayList(Arrays
335                                     .asList(currentParamValues));
336                                 paramList.add(translatedParamValue);
337                                 String newParamValues[] = new String[paramList
338                                     .size()];
339                                 queryParams.put(paramName, paramList
340                                     .toArray(newParamValues));
341                             }
342                         } else {
343                             queryParams.put(paramName, translatedParamValue);
344                         }
345                     }
346                 }
347             }
348         }
349         return queryParams;
350     }
351 }