001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org)
002 // The Molly framework is freely distributable under the terms of an
003 // MIT-style license. For details, see the molly pages web site at:
004 // http://www.mollypages.org/. Use, modify, have fun !
005
006 package fc.web.servlet;
007
008 import java.util.*;
009 import java.util.logging.*;
010 import java.io.*;
011 import javax.servlet.*;
012 import javax.servlet.http.*;
013 import java.net.*;
014
015 import fc.io.*;
016
017 /**
018 Misc web related utility functions
019
020 @author hursh jain
021 **/
022 public final class WebUtil
023 {
024 final static Log log = Log.get("fc.web.servlet");
025
026 //--------------------- getParameters ----------------------
027
028 /**
029 Gets the specified required initialization parameter from
030 the servlet context. If this param is not found, throws a
031 permanent {@link javax.servlet.UnavailableException}.
032 **/
033 public static final String getRequiredParam
034 (ServletContext context, String name)
035 throws ServletException
036 {
037 String param = context.getInitParameter(name);
038
039 if (param == null) {
040 String error = "Required Servlet Init Parameter: '" + name + "' was not found";
041 log.error(error);
042 throw new UnavailableException(error);
043 }
044
045 return param;
046 }
047
048 /**
049 Gets the specified initialization parameter from the servlet context. If
050 this param is not found, then the specified backup value is returned.
051 **/
052 public static final String getParam(
053 ServletContext context, String name, String backup)
054 {
055 String param = context.getInitParameter(name);
056 if (param != null)
057 return param;
058 else
059 return backup;
060 }
061
062 /**
063 Gets the specified initialization parameter for the specified servlet
064 (via it's ServletConfig). Note, servlet init parameters are servlet
065 specific whereas context parameters are shared by all servlets within
066 that context. If this param is not found, throws a permanent {@link
067 javax.servlet.UnavailableException}.
068 **/
069 public static final String getRequiredParam(Servlet servlet, String name)
070 throws ServletException
071 {
072 ServletConfig config = servlet.getServletConfig();
073 String param = config.getInitParameter(name);
074
075 if (param == null) {
076 String error = "Required Servlet Init Parameter: '" + name + "' for servlet '" + config.getServletName() + "' was not found";
077 log.error(error);
078 throw new UnavailableException(error);
079 }
080
081 return param;
082 }
083
084 /**
085 Gets the specified initialization parameter for the specified servlet
086 (via it's ServletConfig). Note, servlet init parameters are servlet
087 specific whereas context parameters are shared by all servlets within
088 that context. If this param is not found, then the specified backup value
089 is returned.
090 **/
091 public static final String getParam(
092 Servlet servlet, String name, String backup)
093 {
094 ServletConfig config = servlet.getServletConfig();
095 String param = config.getInitParameter(name);
096 if (param != null)
097 return param;
098 else
099 return backup;
100 }
101
102 /**
103 Gets the specified required parameter from the request
104 object. If this param is not found, throws a ServletException
105 **/
106 public static final String getRequiredParam(
107 HttpServletRequest request, String name)
108 throws ServletException
109 {
110 String param = request.getParameter(name);
111
112 if (param == null) {
113 String error = "Required Parameter: '" + name + "' was not found";
114 log.error(error);
115 throw new ServletException(error);
116 }
117
118 return param;
119 }
120
121 /**
122 Gets the specified parameter from the request object.
123 If this param is not found, returns the backup value.
124 **/
125 public static final String getParam(
126 HttpServletRequest request, String name, String backup)
127 {
128 String param = request.getParameter(name);
129 if (param != null)
130 return param;
131 else
132 return backup;
133 }
134
135 /**
136 The returned value will be <tt>true</tt> if the specified parameter
137 is present in the request and is a non-null non-empty string (<b>of
138 any value</b>). This is useful for radioboxes and checkboxes.
139 **/
140 public static final boolean isSelected(
141 HttpServletRequest request, String name)
142 {
143 String param = request.getParameter(name);
144 if (param == null || param.equals(""))
145 return false;
146
147 return true;
148 }
149
150 /**
151 Convenience method that returns the specified parameter as a boolean
152 value. The returned value will be converted via the
153 {@link Boolean#parseBoolean(String)} (for <tt>true</tt>, the
154 value should be non-null and equal ignore case to "true").
155 **/
156 public static final boolean getBooleanParameter(
157 HttpServletRequest request, String name)
158 {
159 String param = request.getParameter(name);
160 return Boolean.parseBoolean(param);
161 }
162
163
164 //--------------------- getAttributes ----------------------
165
166 /**
167 Gets the specified required attribute from the servlet context. If
168 this attribute is not found, throws a permanent
169 {@link javax.servlet.UnavailableException}.
170 **/
171 public static final Object getRequiredAttribute(
172 ServletContext context, String name)
173 throws ServletException
174 {
175 Object param = context.getAttribute(name);
176
177 if (param == null) {
178 String error = "Required context attribute: '" + name + "' was not found";
179 log.error(error);
180 throw new UnavailableException(error);
181 }
182
183 return param;
184 }
185
186 /**
187 Gets the specified attribute from the servlet context. If this attribute
188 is not found, then the specified backup value is returned.
189 **/
190 public static final Object getAttribute(
191 ServletContext context, String name, String backup)
192 throws ServletException
193 {
194 Object param = context.getAttribute(name);
195
196 if (param != null)
197 return param;
198 else
199 return backup;
200 }
201
202 /**
203 Gets the specified required attribute from the request object.
204 If this param is not found, throws a ServletException.
205 **/
206 public static final Object getRequiredAttribute(
207 HttpServletRequest request, String name)
208 throws ServletException
209 {
210 Object param = request.getAttribute(name);
211
212 if (param == null) {
213 String error = "Required Parameter: '" + name + "' was not found";
214 log.error(error);
215 throw new ServletException(error);
216 }
217
218 return param;
219 }
220
221 /**
222 Gets the specified attribute from the request object.
223 If this attribute is not found, returns the backup value.
224 **/
225 public static final Object getAttribute(
226 HttpServletRequest request, String name, String backup)
227 throws ServletException
228 {
229 Object param = request.getAttribute(name);
230 if (param != null)
231 return param;
232 else
233 return backup;
234 }
235
236 //----------------------------------------------------------
237
238 /**
239 Returns a HttpSession attribute as a String or null if the attribute
240 was absent.
241
242 @param name the name of the session attribute
243 @throws ClassCastException if the attribute was not a String
244 @throws NullPointerException if the specified session or name parameters
245 were null
246 **/
247 public static final String getSessionString(HttpSession session, String name)
248 {
249 Object obj = session.getAttribute(name);
250 if (obj != null)
251 return (String) obj;
252 else
253 return null;
254 }
255
256 /**
257 Returns the cookie with the specified name or <tt>null</tt> if no cookie
258 was found.
259 */
260 public static Cookie getCookie(HttpServletRequest req, String cookieName)
261 {
262 Cookie[] cookies = req.getCookies();
263
264 if (cookies == null)
265 return null;
266
267 for (int n = 0; n < cookies.length; n++)
268 {
269 if (cookies[n].getName().equals(cookieName))
270 {
271 return cookies[n];
272 }
273 }
274 return null;
275 }
276
277 /**
278 Redirects the request (server side redirect) to the specified relative URL
279 via the {@link javax.servlet.ServletRequest#getRequestDispatcher}. See
280 {@link javax.servlet.RequestDispatcher#forward} for restrictions while
281 forwarding to another page (in particular, the response must not have been
282 committed before calling this method).
283 <p>
284 The calling code <b>must</b> have a <tt>return</tt> statement immediately
285 after calling this method, otherwise RequestDispatching will probably not
286 work as intended.
287
288 @param req the current request
289 @param res the current response
290 @param URL The pathname specified may be relative, although it
291 cannot extend outside the current servlet context. If the
292 path begins with a "/" it is interpreted as starting from
293 the root of the current context. (i.e., the webapp context,
294 if present, does not need to be specified).
295 **/
296 public static final void forward ( HttpServletRequest req,
297 HttpServletResponse res,
298 String URL)
299 throws ServletException, IOException
300 {
301 RequestDispatcher rd = req.getRequestDispatcher(URL);
302 if (log.canLog(Log.DEBUG)) {
303 StringBuffer buf = getRequestURL(req);
304 log.bug(buf, "forwarding to page: ", URL);
305 }
306 rd.forward(req, res);
307 }
308
309 /**
310 Redirects the browser to the specified URL via a client side redirect URL.
311 Automatically creates a full URL <u>(including the webapp context path)</u>
312 suitable for this purpose. This method is a thin wrapper around {@link
313 javax.servlet.http.HttpServletResponse#sendRedirect Response.sentRedirect}
314 <p>
315 Session information is preserved if using container provided URL-based sessions.
316 <p>
317 <u>If the response has already been committed, this method throws an
318 <tt>IllegalStateException</tt></u>. After using this method, the response should be
319 considered to be committed and should not be written to.
320
321 @param req the current request
322 @param res the current response
323 @param location See
324 {@link javax.servlet.http.HttpResponse#sendRedirect}.
325 Essentially, the location can be relative to the
326 specified request's URI or relative to the
327 context root if it contains a leading '/'
328 **/
329 public static final void clientRedirect(
330 HttpServletRequest req, HttpServletResponse res, String location)
331 throws ServletException, IOException
332 {
333 String redirectURL = res.encodeRedirectURL(
334 absolutePath(req, location));
335 if (log.canLog(Log.DEBUG)) {
336 StringBuffer buf = getRequestURL(req);
337 log.bug(buf, "redirecting to page: ", redirectURL);
338 }
339 res.sendRedirect(redirectURL);
340 }
341
342 /**
343 Creates an absolute path starting from the web document root directory. It
344 does so by prepending the webapp context path (if any) of the specified
345 request to the specified path. The path is <b>not</b> URL encoded with
346 session informtion (see {@link #absoluteEncPath}).
347 <p>
348 This method should be used for all HTML links (in html pages and forms)
349 and also for client side redirects. This ensures the ability to move the calling code
350 to a different web app (other than the default root ("/") web app). If
351 it's certain that there will always only be 1 web app (the default)
352 then calling this method is not necessary.
353 <p>
354 Note also that server-side redirects and includes (with RequestDispatcher)
355 should *not* use this method since all such server side redirects etc
356 automatically work as expected inside the web app context and are always
357 relative to the web app context itself.
358
359 @param req the current HttpServletRequest
360 @param path an absolute path starting from the root of
361 the context associated with the request.
362 If the current webapp (context) is <tt>/foo</tt> and
363 the specified path is <tt>/bar</tt>, then this
364 method will return <tt>/foo/bar</tt>
365 **/
366 public static final String absolutePath(HttpServletRequest req, String path)
367 {
368 return req.getContextPath() + path;
369 }
370
371 /**
372 URL encodes and returns the path obtained via {@link #absolutePath}.
373 **/
374 public static final String absoluteEncPath(
375 HttpServletRequest req, HttpServletResponse res, String path)
376 {
377 return res.encodeURL(absolutePath(req, path));
378 }
379
380 /**
381 @return the full requested URL including any query parameters.
382 (the {@link
383 javax.servlet.Http.HttpServletRequest.getRequestURL() RequestURL}
384 method in HttpServletRequest does not return the query part of the URL)
385 **/
386 public static final StringBuffer getRequestURL(HttpServletRequest req)
387 {
388 StringBuffer buf = req.getRequestURL();
389 String qs = req.getQueryString();
390 if (qs !=null) {
391 buf.append('?');
392 buf.append(qs);
393 }
394 return buf;
395 }
396
397
398 //== HTML Form related ==========================
399
400 private static final String confirm = "confirm";
401 private static final String cancel = "cancel";
402
403 /**
404 Returns <tt>true</tt> only if a request parameter with the
405 name <tt>confirm</tt> is seen. Forms can set a submit button
406 with this name to signal a confirm request.
407 **/
408 public static final boolean requestCancelled(HttpServletRequest req) {
409 String str = req.getParameter(cancel);
410 return (str != null); //testing for key not value
411 }
412
413 /**
414 Returns <tt>true</tt> only if a request parameter with the
415 name <tt>cancel</tt> is seen. Forms can set a submit button
416 with this name to signal a cancel request.
417 **/
418 public static final boolean requestConfirmed(HttpServletRequest req) {
419 String str = req.getParameter(confirm);
420 return (str != null); //testing for key not value
421 }
422
423 /**
424 writes all request params received in the specied request to
425 <tt>System.out</tt>. Useful for debugging.
426 **/
427 public static final void debugRequestParams(HttpServletRequest req)
428 {
429 debugRequestParams(req, System.out);
430 }
431
432
433 /**
434 writes all request params received in the specied request to
435 the specified output stream. Useful for debugging.
436 **/
437 public static final void debugRequestParams(HttpServletRequest req, OutputStream out)
438 {
439 System.out.print("Request params/");
440 System.out.print(Thread.currentThread().getName());
441 System.out.print(" ");
442
443 Iterator i = req.getParameterMap().entrySet().iterator();
444 while (i.hasNext()) {
445 Map.Entry me = (Map.Entry) i.next();
446 System.out.print(me.getKey());
447 System.out.print(":");
448 System.out.print(Arrays.toString((String[])me.getValue()));
449
450 if (i.hasNext())
451 System.out.print(", ");
452 else
453 System.out.println();
454 }
455 }
456
457
458 /**
459 set expire headers to cover all cases
460 **/
461 public static final void setExpireHeaders(HttpServletResponse res)
462 {
463 // Set standard HTTP/1.1 no-cache headers.
464 res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
465 // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
466 res.addHeader("Cache-Control", "post-check=0, pre-check=0");
467 // Set standard HTTP/1.0 no-cache header.
468 res.setHeader("Pragma", "no-cache");
469 //expire in past
470 res.setHeader("Expires", "-1");
471 }
472
473 /*
474 Parse a query parameter string (useful for things like websocket and custom
475 servers. The query param should be url form encoded and is decoded using
476 UTF-8 (via {@link java.net.URLDecoder}). Returns a map containing each
477 <tt>name:value</tt> pair
478 */
479 public static Map parseQuery(String query) throws UnsupportedEncodingException
480 {
481 Map m = new HashMap();
482 String[] params = query.split("&");
483 for (int n = 0; n < params.length; n++) {
484 String param = params[n];
485 int i = param.indexOf("=");
486 m.put(
487 URLDecoder.decode(param.substring(0, i), "UTF-8"),
488 URLDecoder.decode(param.substring(i + 1), "UTF-8"));
489 }
490 return m;
491 }
492
493 /**
494 Returns true if the client is a mobile device
495 */
496 public static boolean isMobileClient(HttpServletRequest req)
497 {
498 //https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
499
500 //name is case insensitve (in servlet and http spec), different browsers send different cases so that's good
501 String user_agent = req.getHeader("user-agent");
502 return user_agent != null && user_agent.indexOf("obile") > 0; //Mobile or mobile
503 }
504
505 /**
506 Returns true if the client is an iOS mobile device.
507 */
508 public static boolean isIOSClient(HttpServletRequest req)
509 {
510 //https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
511
512 //name is case insensitve (in servlet and http spec), different browsers send different cases so that's good
513 String user_agent = req.getHeader("user-agent");
514 return user_agent != null && user_agent.indexOf("obile") > 0 && user_agent.indexOf("afari") > 0;
515 }
516
517 } //~class WebUtil
518