001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.portlet.util; 018 019import java.io.File; 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.util.Enumeration; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.TreeMap; 026import javax.portlet.ActionRequest; 027import javax.portlet.ActionResponse; 028import javax.portlet.PortletContext; 029import javax.portlet.PortletException; 030import javax.portlet.PortletRequest; 031import javax.portlet.PortletRequestDispatcher; 032import javax.portlet.PortletResponse; 033import javax.portlet.PortletSession; 034import javax.portlet.ResourceRequest; 035import javax.portlet.ResourceResponse; 036import javax.portlet.filter.PortletRequestWrapper; 037import javax.portlet.filter.PortletResponseWrapper; 038import javax.servlet.http.Cookie; 039 040import org.springframework.util.Assert; 041import org.springframework.util.StringUtils; 042import org.springframework.web.util.WebUtils; 043 044/** 045 * Miscellaneous utilities for portlet applications. 046 * Used by various framework classes. 047 * 048 * @author Juergen Hoeller 049 * @author William G. Thompson, Jr. 050 * @author John A. Lewis 051 * @since 2.0 052 */ 053public abstract class PortletUtils { 054 055 /** 056 * Return the temporary directory for the current web application, 057 * as provided by the portlet container. 058 * @param portletContext the portlet context of the web application 059 * @return the File representing the temporary directory 060 */ 061 public static File getTempDir(PortletContext portletContext) { 062 Assert.notNull(portletContext, "PortletContext must not be null"); 063 return (File) portletContext.getAttribute(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE); 064 } 065 066 /** 067 * Return the real path of the given path within the web application, 068 * as provided by the portlet container. 069 * <p>Prepends a slash if the path does not already start with a slash, 070 * and throws a {@link java.io.FileNotFoundException} if the path cannot 071 * be resolved to a resource (in contrast to 072 * {@link javax.portlet.PortletContext#getRealPath PortletContext's {@code getRealPath}}, 073 * which simply returns {@code null}). 074 * @param portletContext the portlet context of the web application 075 * @param path the relative path within the web application 076 * @return the corresponding real path 077 * @throws FileNotFoundException if the path cannot be resolved to a resource 078 * @see javax.portlet.PortletContext#getRealPath 079 */ 080 public static String getRealPath(PortletContext portletContext, String path) throws FileNotFoundException { 081 Assert.notNull(portletContext, "PortletContext must not be null"); 082 // Interpret location as relative to the web application root directory. 083 if (!path.startsWith("/")) { 084 path = "/" + path; 085 } 086 String realPath = portletContext.getRealPath(path); 087 if (realPath == null) { 088 throw new FileNotFoundException( 089 "PortletContext resource [" + path + "] cannot be resolved to absolute file path - " + 090 "web application archive not expanded?"); 091 } 092 return realPath; 093 } 094 095 096 /** 097 * Check the given request for a session attribute of the given name under the 098 * {@link javax.portlet.PortletSession#PORTLET_SCOPE}. 099 * Returns {@code null} if there is no session or if the session has no such attribute in that scope. 100 * Does not create a new session if none has existed before! 101 * @param request current portlet request 102 * @param name the name of the session attribute 103 * @return the value of the session attribute, or {@code null} if not found 104 */ 105 public static Object getSessionAttribute(PortletRequest request, String name) { 106 return getSessionAttribute(request, name, PortletSession.PORTLET_SCOPE); 107 } 108 109 /** 110 * Check the given request for a session attribute of the given name in the given scope. 111 * Returns {@code null} if there is no session or if the session has no such attribute in that scope. 112 * Does not create a new session if none has existed before! 113 * @param request current portlet request 114 * @param name the name of the session attribute 115 * @param scope session scope of this attribute 116 * @return the value of the session attribute, or {@code null} if not found 117 */ 118 public static Object getSessionAttribute(PortletRequest request, String name, int scope) { 119 Assert.notNull(request, "Request must not be null"); 120 PortletSession session = request.getPortletSession(false); 121 return (session != null ? session.getAttribute(name, scope) : null); 122 } 123 124 /** 125 * Check the given request for a session attribute of the given name 126 * under the {@link javax.portlet.PortletSession#PORTLET_SCOPE}. 127 * Throws an exception if there is no session or if the session has 128 * no such attribute in that scope. 129 * <p>Does not create a new session if none has existed before! 130 * @param request current portlet request 131 * @param name the name of the session attribute 132 * @return the value of the session attribute 133 * @throws IllegalStateException if the session attribute could not be found 134 */ 135 public static Object getRequiredSessionAttribute(PortletRequest request, String name) 136 throws IllegalStateException { 137 138 return getRequiredSessionAttribute(request, name, PortletSession.PORTLET_SCOPE); 139 } 140 141 /** 142 * Check the given request for a session attribute of the given name in the given scope. 143 * Throws an exception if there is no session or if the session has no such attribute 144 * in that scope. 145 * <p>Does not create a new session if none has existed before! 146 * @param request current portlet request 147 * @param name the name of the session attribute 148 * @param scope session scope of this attribute 149 * @return the value of the session attribute 150 * @throws IllegalStateException if the session attribute could not be found 151 */ 152 public static Object getRequiredSessionAttribute(PortletRequest request, String name, int scope) 153 throws IllegalStateException { 154 Object attr = getSessionAttribute(request, name, scope); 155 if (attr == null) { 156 throw new IllegalStateException("No session attribute '" + name + "' found"); 157 } 158 return attr; 159 } 160 161 /** 162 * Set the session attribute with the given name to the given value under the {@link javax.portlet.PortletSession#PORTLET_SCOPE}. 163 * Removes the session attribute if value is {@code null}, if a session existed at all. 164 * Does not create a new session if not necessary! 165 * @param request current portlet request 166 * @param name the name of the session attribute 167 * @param value the value of the session attribute 168 */ 169 public static void setSessionAttribute(PortletRequest request, String name, Object value) { 170 setSessionAttribute(request, name, value, PortletSession.PORTLET_SCOPE); 171 } 172 173 /** 174 * Set the session attribute with the given name to the given value in the given scope. 175 * Removes the session attribute if value is {@code null}, if a session existed at all. 176 * Does not create a new session if not necessary! 177 * @param request current portlet request 178 * @param name the name of the session attribute 179 * @param value the value of the session attribute 180 * @param scope session scope of this attribute 181 */ 182 public static void setSessionAttribute(PortletRequest request, String name, Object value, int scope) { 183 Assert.notNull(request, "Request must not be null"); 184 if (value != null) { 185 request.getPortletSession().setAttribute(name, value, scope); 186 } 187 else { 188 PortletSession session = request.getPortletSession(false); 189 if (session != null) { 190 session.removeAttribute(name, scope); 191 } 192 } 193 } 194 195 /** 196 * Get the specified session attribute under the {@link javax.portlet.PortletSession#PORTLET_SCOPE}, 197 * creating and setting a new attribute if no existing found. The given class 198 * needs to have a public no-arg constructor. 199 * Useful for on-demand state objects in a web tier, like shopping carts. 200 * @param session current portlet session 201 * @param name the name of the session attribute 202 * @param clazz the class to instantiate for a new attribute 203 * @return the value of the session attribute, newly created if not found 204 * @throws IllegalArgumentException if the session attribute could not be instantiated 205 */ 206 public static Object getOrCreateSessionAttribute(PortletSession session, String name, Class<?> clazz) 207 throws IllegalArgumentException { 208 209 return getOrCreateSessionAttribute(session, name, clazz, PortletSession.PORTLET_SCOPE); 210 } 211 212 /** 213 * Get the specified session attribute in the given scope, 214 * creating and setting a new attribute if no existing found. The given class 215 * needs to have a public no-arg constructor. 216 * Useful for on-demand state objects in a web tier, like shopping carts. 217 * @param session current portlet session 218 * @param name the name of the session attribute 219 * @param clazz the class to instantiate for a new attribute 220 * @param scope the session scope of this attribute 221 * @return the value of the session attribute, newly created if not found 222 * @throws IllegalArgumentException if the session attribute could not be instantiated 223 */ 224 public static Object getOrCreateSessionAttribute(PortletSession session, String name, Class<?> clazz, int scope) 225 throws IllegalArgumentException { 226 227 Assert.notNull(session, "Session must not be null"); 228 Object sessionObject = session.getAttribute(name, scope); 229 if (sessionObject == null) { 230 Assert.notNull(clazz, "Class must not be null if attribute value is to be instantiated"); 231 try { 232 sessionObject = clazz.newInstance(); 233 } 234 catch (InstantiationException ex) { 235 throw new IllegalArgumentException( 236 "Could not instantiate class [" + clazz.getName() + 237 "] for session attribute '" + name + "': " + ex.getMessage()); 238 } 239 catch (IllegalAccessException ex) { 240 throw new IllegalArgumentException( 241 "Could not access default constructor of class [" + clazz.getName() + 242 "] for session attribute '" + name + "': " + ex.getMessage()); 243 } 244 session.setAttribute(name, sessionObject, scope); 245 } 246 return sessionObject; 247 } 248 249 /** 250 * Return the best available mutex for the given session: 251 * that is, an object to synchronize on for the given session. 252 * <p>Returns the session mutex attribute if available; usually, 253 * this means that the 254 * {@link org.springframework.web.util.HttpSessionMutexListener} 255 * needs to be defined in {@code web.xml}. Falls back to the 256 * {@link javax.portlet.PortletSession} itself if no mutex attribute found. 257 * <p>The session mutex is guaranteed to be the same object during 258 * the entire lifetime of the session, available under the key defined 259 * by the {@link org.springframework.web.util.WebUtils#SESSION_MUTEX_ATTRIBUTE} 260 * constant. It serves as a safe reference to synchronize on for locking 261 * on the current session. 262 * <p>In many cases, the {@link javax.portlet.PortletSession} reference 263 * itself is a safe mutex as well, since it will always be the same 264 * object reference for the same active logical session. However, this is 265 * not guaranteed across different servlet containers; the only 100% safe 266 * way is a session mutex. 267 * @param session the HttpSession to find a mutex for 268 * @return the mutex object (never {@code null}) 269 * @see org.springframework.web.util.WebUtils#SESSION_MUTEX_ATTRIBUTE 270 * @see org.springframework.web.util.HttpSessionMutexListener 271 */ 272 public static Object getSessionMutex(PortletSession session) { 273 Assert.notNull(session, "Session must not be null"); 274 Object mutex = session.getAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE, PortletSession.APPLICATION_SCOPE); 275 if (mutex == null) { 276 mutex = session; 277 } 278 return mutex; 279 } 280 281 282 /** 283 * Return an appropriate request object of the specified type, if available, 284 * unwrapping the given request as far as necessary. 285 * @param request the portlet request to introspect 286 * @param requiredType the desired type of request object 287 * @return the matching request object, or {@code null} if none 288 * of that type is available 289 */ 290 @SuppressWarnings("unchecked") 291 public static <T> T getNativeRequest(PortletRequest request, Class<T> requiredType) { 292 if (requiredType != null) { 293 if (requiredType.isInstance(request)) { 294 return (T) request; 295 } 296 else if (request instanceof PortletRequestWrapper) { 297 return getNativeRequest(((PortletRequestWrapper) request).getRequest(), requiredType); 298 } 299 } 300 return null; 301 } 302 303 /** 304 * Return an appropriate response object of the specified type, if available, 305 * unwrapping the given response as far as necessary. 306 * @param response the portlet response to introspect 307 * @param requiredType the desired type of response object 308 * @return the matching response object, or {@code null} if none 309 * of that type is available 310 */ 311 @SuppressWarnings("unchecked") 312 public static <T> T getNativeResponse(PortletResponse response, Class<T> requiredType) { 313 if (requiredType != null) { 314 if (requiredType.isInstance(response)) { 315 return (T) response; 316 } 317 else if (response instanceof PortletResponseWrapper) { 318 return getNativeResponse(((PortletResponseWrapper) response).getResponse(), requiredType); 319 } 320 } 321 return null; 322 } 323 324 /** 325 * Expose the given Map as request attributes, using the keys as attribute names 326 * and the values as corresponding attribute values. Keys must be Strings. 327 * @param request current portlet request 328 * @param attributes the attributes Map 329 */ 330 public static void exposeRequestAttributes(PortletRequest request, Map<String, ?> attributes) { 331 Assert.notNull(request, "Request must not be null"); 332 Assert.notNull(attributes, "Attributes Map must not be null"); 333 for (Map.Entry<String, ?> entry : attributes.entrySet()) { 334 request.setAttribute(entry.getKey(), entry.getValue()); 335 } 336 } 337 338 /** 339 * Retrieve the first cookie with the given name. Note that multiple 340 * cookies can have the same name but different paths or domains. 341 * @param request current portlet request 342 * @param name cookie name 343 * @return the first cookie with the given name, or {@code null} if none is found 344 */ 345 public static Cookie getCookie(PortletRequest request, String name) { 346 Assert.notNull(request, "Request must not be null"); 347 Cookie cookies[] = request.getCookies(); 348 if (cookies != null) { 349 for (Cookie cookie : cookies) { 350 if (name.equals(cookie.getName())) { 351 return cookie; 352 } 353 } 354 } 355 return null; 356 } 357 358 /** 359 * Check if a specific input type="submit" parameter was sent in the request, 360 * either via a button (directly with name) or via an image (name + ".x" or 361 * name + ".y"). 362 * @param request current portlet request 363 * @param name name of the parameter 364 * @return if the parameter was sent 365 * @see org.springframework.web.util.WebUtils#SUBMIT_IMAGE_SUFFIXES 366 */ 367 public static boolean hasSubmitParameter(PortletRequest request, String name) { 368 return getSubmitParameter(request, name) != null; 369 } 370 371 /** 372 * Return the full name of a specific input type="submit" parameter 373 * if it was sent in the request, either via a button (directly with name) 374 * or via an image (name + ".x" or name + ".y"). 375 * @param request current portlet request 376 * @param name name of the parameter 377 * @return the actual parameter name with suffix if needed - null if not present 378 * @see org.springframework.web.util.WebUtils#SUBMIT_IMAGE_SUFFIXES 379 */ 380 public static String getSubmitParameter(PortletRequest request, String name) { 381 Assert.notNull(request, "Request must not be null"); 382 if (request.getParameter(name) != null) { 383 return name; 384 } 385 for (int i = 0; i < WebUtils.SUBMIT_IMAGE_SUFFIXES.length; i++) { 386 String suffix = WebUtils.SUBMIT_IMAGE_SUFFIXES[i]; 387 String parameter = name + suffix; 388 if (request.getParameter(parameter) != null) { 389 return parameter; 390 } 391 } 392 return null; 393 } 394 395 /** 396 * Return a map containing all parameters with the given prefix. 397 * Maps single values to String and multiple values to String array. 398 * <p>For example, with a prefix of "spring_", "spring_param1" and 399 * "spring_param2" result in a Map with "param1" and "param2" as keys. 400 * <p>Similar to portlet 401 * {@link javax.portlet.PortletRequest#getParameterMap()}, 402 * but more flexible. 403 * @param request portlet request in which to look for parameters 404 * @param prefix the beginning of parameter names 405 * (if this is {@code null} or the empty string, all parameters will match) 406 * @return map containing request parameters <b>without the prefix</b>, 407 * containing either a String or a String array as values 408 * @see javax.portlet.PortletRequest#getParameterNames 409 * @see javax.portlet.PortletRequest#getParameterValues 410 * @see javax.portlet.PortletRequest#getParameterMap 411 */ 412 public static Map<String, Object> getParametersStartingWith(PortletRequest request, String prefix) { 413 Assert.notNull(request, "Request must not be null"); 414 Enumeration<String> paramNames = request.getParameterNames(); 415 Map<String, Object> params = new TreeMap<String, Object>(); 416 if (prefix == null) { 417 prefix = ""; 418 } 419 while (paramNames != null && paramNames.hasMoreElements()) { 420 String paramName = paramNames.nextElement(); 421 if ("".equals(prefix) || paramName.startsWith(prefix)) { 422 String unprefixed = paramName.substring(prefix.length()); 423 String[] values = request.getParameterValues(paramName); 424 if (values == null || values.length == 0) { 425 // Do nothing, no values found at all. 426 } 427 else if (values.length > 1) { 428 params.put(unprefixed, values); 429 } 430 else { 431 params.put(unprefixed, values[0]); 432 } 433 } 434 } 435 return params; 436 } 437 438 /** 439 * Return the target page specified in the request. 440 * @param request current portlet request 441 * @param paramPrefix the parameter prefix to check for 442 * (e.g. "_target" for parameters like "_target1" or "_target2") 443 * @param currentPage the current page, to be returned as fallback 444 * if no target page specified 445 * @return the page specified in the request, or current page if not found 446 */ 447 public static int getTargetPage(PortletRequest request, String paramPrefix, int currentPage) { 448 Enumeration<String> paramNames = request.getParameterNames(); 449 while (paramNames.hasMoreElements()) { 450 String paramName = paramNames.nextElement(); 451 if (paramName.startsWith(paramPrefix)) { 452 for (int i = 0; i < WebUtils.SUBMIT_IMAGE_SUFFIXES.length; i++) { 453 String suffix = WebUtils.SUBMIT_IMAGE_SUFFIXES[i]; 454 if (paramName.endsWith(suffix)) { 455 paramName = paramName.substring(0, paramName.length() - suffix.length()); 456 } 457 } 458 return Integer.parseInt(paramName.substring(paramPrefix.length())); 459 } 460 } 461 return currentPage; 462 } 463 464 465 /** 466 * Pass all the action request parameters to the render phase by putting them into 467 * the action response object. This may not be called when the action will call 468 * {@link javax.portlet.ActionResponse#sendRedirect sendRedirect}. 469 * @param request the current action request 470 * @param response the current action response 471 * @see javax.portlet.ActionResponse#setRenderParameter 472 */ 473 public static void passAllParametersToRenderPhase(ActionRequest request, ActionResponse response) { 474 try { 475 Enumeration<String> en = request.getParameterNames(); 476 while (en.hasMoreElements()) { 477 String param = en.nextElement(); 478 String values[] = request.getParameterValues(param); 479 response.setRenderParameter(param, values); 480 } 481 } 482 catch (IllegalStateException ex) { 483 // Ignore in case sendRedirect was already set. 484 } 485 } 486 487 /** 488 * Clear all the render parameters from the {@link javax.portlet.ActionResponse}. 489 * This may not be called when the action will call 490 * {@link ActionResponse#sendRedirect sendRedirect}. 491 * @param response the current action response 492 * @see ActionResponse#setRenderParameters 493 */ 494 public static void clearAllRenderParameters(ActionResponse response) { 495 try { 496 response.setRenderParameters(new HashMap<String, String[]>(0)); 497 } 498 catch (IllegalStateException ex) { 499 // Ignore in case sendRedirect was already set. 500 } 501 } 502 503 /** 504 * Serve the resource as specified in the given request to the given response, 505 * using the PortletContext's request dispatcher. 506 * <p>This is roughly equivalent to Portlet 2.0 GenericPortlet. 507 * @param request the current resource request 508 * @param response the current resource response 509 * @param context the current Portlet's PortletContext 510 * @throws PortletException propagated from Portlet API's forward method 511 * @throws IOException propagated from Portlet API's forward method 512 */ 513 public static void serveResource(ResourceRequest request, ResourceResponse response, PortletContext context) 514 throws PortletException, IOException { 515 516 String id = request.getResourceID(); 517 if (id != null) { 518 if (!PortletUtils.isProtectedResource(id)) { 519 PortletRequestDispatcher rd = context.getRequestDispatcher(id); 520 if (rd != null) { 521 rd.forward(request, response); 522 return; 523 } 524 } 525 response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "404"); 526 } 527 } 528 529 /** 530 * Check whether the specified path indicates a resource in the protected 531 * WEB-INF or META-INF directories. 532 * @param path the path to check 533 */ 534 private static boolean isProtectedResource(String path) { 535 return (StringUtils.startsWithIgnoreCase(path, "/WEB-INF") || 536 StringUtils.startsWithIgnoreCase(path, "/META-INF")); 537 } 538 539}