001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.util;
006:
007: import org.acegisecurity.Authentication;
008: import org.acegisecurity.context.SecurityContextHolder;
009: import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
010: import org.acegisecurity.userdetails.UserDetails;
011: import org.vfny.geoserver.global.GeoServer;
012: import org.vfny.geoserver.global.UserContainer;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.net.HttpURLConnection;
016: import java.net.MalformedURLException;
017: import java.net.URL;
018: import java.net.URLConnection;
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.StringTokenizer;
024: import java.util.logging.Logger;
025: import java.util.zip.GZIPInputStream;
026: import java.util.zip.Inflater;
027: import java.util.zip.InflaterInputStream;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpSession;
030:
031: /**
032: * Utility methods helpful when processing GeoServer Requests.
033: *
034: * <p>
035: * Provides helper functions and classes useful when implementing your own
036: * Response classes. Of significant importantance are the Request processing
037: * functions that allow access to the WebContainer, GeoServer and the User's
038: * Session.
039: * </p>
040: *
041: * <p>
042: * If you are working with the STRUTS API the Action method is the direct
043: * paralle of the Response classes. You may whish to look at how ConfigAction
044: * is implemented, it is a super class which delegates to these Request
045: * processing methods.
046: * </p>
047: *
048: * @author Jody Garnett
049: */
050: public final class Requests {
051: static Logger LOGGER = org.geotools.util.logging.Logging
052: .getLogger("org.vfny.geoserver");
053:
054: /*
055: * This is the parameter used to get the proxy from the
056: * web.xml file. This is a bit hacky, it should be moved to
057: * GeoServer.java and be a normal config parameter, but the
058: * overhead of making a new config param is just too high,
059: * so we're allowing this to just be read from the web.xml
060: ( See GEOS-598 for more information
061: */
062: public static final String PROXY_PARAM = "PROXY_BASE_URL";
063:
064: /**
065: * Get base url used - it is not any more assumed to be
066: * http://server:port/geoserver
067: *
068: * Removed the hardcoded "http://" and replaced it with
069: * httpServletRequest.getScheme() because the https case was not being
070: * handled.
071: *
072: * @param httpServletRequest
073: * @return http://server:port/path-defined-context
074: */
075: public static String getBaseUrl(
076: HttpServletRequest httpServletRequest, GeoServer geoserver) {
077: // try with the web interface configuration, if it fails, look into
078: // web.xml just to keep compatibility (should be removed next version)
079: // and finally, if nothing is found, give up and return the default base URL
080: String url = ((geoserver != null) ? geoserver.getProxyBaseUrl()
081: : null);
082:
083: if ((geoserver != null) && (url != null)) {
084: url = appendContextPath(url, httpServletRequest
085: .getContextPath());
086: }
087:
088: if ((url == null) || (url.trim().length() == 0)) {
089: if (httpServletRequest != null) {
090: url = httpServletRequest.getSession()
091: .getServletContext().getInitParameter(
092: PROXY_PARAM);
093: }
094:
095: if ((url == null) || (url.trim().length() == 0)) {
096: url = httpServletRequest.getScheme() + "://"
097: + httpServletRequest.getServerName() + ":"
098: + httpServletRequest.getServerPort()
099: + httpServletRequest.getContextPath() + "/";
100: } else {
101: url = appendContextPath(url, httpServletRequest
102: .getContextPath());
103: }
104: }
105:
106: // take care of incompletely setup path
107: if (!url.endsWith("/")) {
108: url = url + "/";
109: }
110:
111: return url;
112: }
113:
114: public static String getBaseJspUrl(
115: HttpServletRequest httpServletRequest, GeoServer geoserver) {
116: // try with the web interface configuration, if it fails, look into
117: // web.xml just to keep compatibility (should be removed next version)
118: // and finally, if nothing is found, give up and return the default base URL
119: String url = geoserver.getProxyBaseUrl();
120:
121: if ((geoserver != null) && (url != null)) {
122: url = appendContextPath(url, httpServletRequest
123: .getRequestURI());
124: }
125:
126: if ((url == null) || (url.trim().length() == 0)) {
127: if (httpServletRequest != null) {
128: url = httpServletRequest.getSession()
129: .getServletContext().getInitParameter(
130: PROXY_PARAM);
131: }
132:
133: if ((url == null) || (url.trim().length() == 0)) {
134: url = httpServletRequest.getScheme() + "://"
135: + httpServletRequest.getServerName() + ":"
136: + httpServletRequest.getServerPort()
137: + httpServletRequest.getRequestURI() + "/";
138: } else {
139: url = appendContextPath(url, httpServletRequest
140: .getRequestURI());
141: }
142: }
143:
144: if (url.endsWith("/")) {
145: url = url.substring(0, url.length() - 1);
146: }
147:
148: return url;
149: }
150:
151: /**
152: * Returns the full url to the tile cache used by GeoServer ( if any ).
153: * <p>
154: * If the tile cache set in the configuration ({@link GeoServer#getTileCache()})
155: * is set to an asbsolute url, it is simply returned. Otherwise the value
156: * is appended to the scheme and host of the supplied <tt>request</tt>.
157: * </p>
158: * @param request The request.
159: * @param geoServer The geoserver configuration.
160: *
161: * @return The url to the tile cache, or <code>null</code> if no tile
162: * cache set.
163: */
164: public static String getTileCacheBaseUrl(
165: HttpServletRequest request, GeoServer geoServer) {
166: //first check if tile cache set
167: String tileCacheBaseUrl = geoServer.getTileCache();
168:
169: if (tileCacheBaseUrl != null) {
170: //two possibilities, local path, or full remote path
171: try {
172: new URL(tileCacheBaseUrl);
173:
174: //full url, return it
175: return tileCacheBaseUrl;
176: } catch (MalformedURLException e1) {
177: //try relative to the same host as request
178: try {
179: String url = appendContextPath(request.getScheme()
180: + "://" + request.getServerName(),
181: tileCacheBaseUrl);
182: new URL(url);
183:
184: //cool return it
185: return url;
186: } catch (MalformedURLException e2) {
187: //out of guesses
188: }
189: }
190: }
191:
192: return null;
193: }
194:
195: /**
196: * Appends a context path to a base url.
197: *
198: * @param url The base url.
199: * @param contextPath The context path to be appended.
200: *
201: * @return A full url with the context path appended.
202: */
203: public static String appendContextPath(String url,
204: String contextPath) {
205: if (url.endsWith("/")) {
206: url = url.substring(0, url.length() - 1);
207: }
208:
209: if (contextPath.startsWith("/")) {
210: contextPath = contextPath.substring(1);
211: }
212:
213: return url + "/" + contextPath;
214: }
215:
216: /**
217: * Appends a query string to a url.
218: * <p>
219: * This method checks <code>url</code> to see if the appended query string requires a '?' or
220: * '&' to be prepended.
221: * </p>
222: *
223: * @param url The base url.
224: * @param queryString The query string to be appended, should not contain the '?' character.
225: *
226: * @return A full url with the query string appended.
227: */
228: public static String appendQueryString(String url,
229: String queryString) {
230: if (url.endsWith("?") || url.endsWith("&")) {
231: return url + queryString;
232: }
233:
234: if (url.indexOf('?') != -1) {
235: return url + "&" + queryString;
236: }
237:
238: return url + "?" + queryString;
239: }
240:
241: /**
242: * Get capabilities base url used
243: *
244: * @param httpServletRequest
245: * @return http://server:port/path-defined-context/data/capabilities
246: */
247: public static String getSchemaBaseUrl(
248: HttpServletRequest httpServletRequest, GeoServer geoserver) {
249: return getBaseUrl(httpServletRequest, geoserver) + "schemas/";
250: }
251:
252: /**
253: * Aquire type safe session information in a UserContainer.
254: *
255: * @param request Http Request used to aquire session reference
256: *
257: * @return UserContainer containing typesafe session information.
258: */
259: public static UserContainer getUserContainer(
260: HttpServletRequest request) {
261: HttpSession session = request.getSession();
262:
263: synchronized (session) {
264: UserContainer user = (UserContainer) session
265: .getAttribute(UserContainer.SESSION_KEY);
266:
267: // acegi variation, login is performed by the acegi subsystem, we do get
268: // the information we need from it
269: if (user == null) {
270: user = new UserContainer();
271:
272: //JD: for some reason there is sometimes a string here. doing
273: // an instanceof check ... although i am not sure why this occurs.
274: final Authentication authentication = SecurityContextHolder
275: .getContext().getAuthentication();
276: if (authentication == null) {
277: LOGGER
278: .warning("Warning, Acegi security subsystem deactivated, no user checks will be made");
279: user.setUsername("admin");
280: } else {
281: Object o = authentication.getPrincipal();
282: if (o instanceof UserDetails) {
283: UserDetails ud = (UserDetails) o;
284: user.setUsername(ud.getUsername());
285: } else if (o instanceof String) {
286: user.setUsername((String) o);
287: }
288: }
289: request.getSession().setAttribute(
290: UserContainer.SESSION_KEY, user);
291: }
292:
293: return user;
294: }
295: }
296:
297: public static boolean loggedIn(HttpServletRequest request) {
298: return !getUserContainer(request).getUsername().equals(
299: "anonymous");
300: }
301:
302: /**
303: * Tests is user is loggin in.
304: *
305: * <p>
306: * True if UserContainer exists has been created.
307: * </p>
308: *
309: * @param request HttpServletRequest providing current Session
310: *
311: * @return
312: */
313: public static boolean isLoggedIn(HttpServletRequest request) {
314: // check the user is not the anonymous one
315: Authentication authentication = SecurityContextHolder
316: .getContext().getAuthentication();
317:
318: return (authentication != null)
319: && !(authentication instanceof AnonymousAuthenticationToken);
320: }
321:
322: /**
323: * Ensures a user is logged out.
324: *
325: * <p>
326: * Removes the UserContainer, and thus GeoServers knowledge of the current
327: * user attached to this Session.
328: * </p>
329: *
330: * @param request HttpServletRequest providing current Session
331: */
332: public static void logOut(HttpServletRequest request) {
333: HttpSession session = request.getSession();
334: session.removeAttribute(UserContainer.SESSION_KEY);
335: }
336:
337: /**
338: * This method gets the correct input stream for a URL.
339: * If the URL is a http/https connection, the Accept-Encoding: gzip, deflate is added.
340: * It the paramter is added, the response is checked to see if the response
341: * is encoded in gzip, deflate or plain bytes. The correct input stream wrapper is then
342: * selected and returned.
343: *
344: * This method was added as part of GEOS-420
345: *
346: * @param url The url to the sld file
347: * @return The InputStream used to validate and parse the SLD xml.
348: * @throws IOException
349: */
350: public static InputStream getInputStream(URL url)
351: throws IOException {
352: //Open the connection
353: URLConnection conn = url.openConnection();
354:
355: //If it is the http or https scheme, then ask for gzip if the server supports it.
356: if (conn instanceof HttpURLConnection) {
357: //Send the requested encoding to the remote server.
358: conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
359: }
360:
361: //Conect to get the response headers
362: conn.connect();
363:
364: //Return the correct inputstream
365: //If the connection is a url, connection, check the response encoding.
366: if (conn instanceof HttpURLConnection) {
367: //Get the content encoding of the server response
368: String encoding = conn.getContentEncoding();
369:
370: //If null, set it to a emtpy string
371: if (encoding == null) {
372: encoding = "";
373: }
374:
375: if (encoding.equalsIgnoreCase("gzip")) {
376: //For gzip input stream, use a GZIPInputStream
377: return new GZIPInputStream(conn.getInputStream());
378: } else if (encoding.equalsIgnoreCase("deflate")) {
379: //If it is encoded as deflate, then select the inflater inputstream.
380: return new InflaterInputStream(conn.getInputStream(),
381: new Inflater(true));
382: } else {
383: //Else read the raw bytes
384: return conn.getInputStream();
385: }
386: } else {
387: //Else read the raw bytes.
388: return conn.getInputStream();
389: }
390: }
391:
392: /**
393: * Parses an 'option-holding' parameters in the following form
394: * FORMAT_OPTIONS=multiKey:val1,val2,val3;singleKey:val
395: *
396: * Useful for parsing out the FORMAT_OPTIONS and LEGEND_OPTIONS parameters
397: */
398: public static Map parseOptionParameter(String rawOptionString)
399: throws IllegalArgumentException {
400: HashMap map = new HashMap();
401: if (rawOptionString == null) {
402: return map;
403: }
404:
405: StringTokenizer semiColonSplitter = new StringTokenizer(
406: rawOptionString, ";");
407: while (semiColonSplitter.hasMoreElements()) {
408: String curKVP = semiColonSplitter.nextToken();
409:
410: final int cloc = curKVP.indexOf(":");
411: if (cloc <= 0) {
412: throw new IllegalArgumentException(
413: "Key-value-pair: '"
414: + curKVP
415: + "' isn't properly formed. It must be of the form 'Key:Value1,Value2...'");
416: }
417: String key = curKVP.substring(0, cloc);
418: String values = curKVP.substring(cloc + 1, curKVP.length());
419: if (values.indexOf(",") != -1) {
420: List valueList = new ArrayList();
421: StringTokenizer commaSplitter = new StringTokenizer(
422: values, ",");
423: while (commaSplitter.hasMoreElements())
424: valueList.add(commaSplitter.nextToken());
425:
426: map.put(key, valueList);
427: } else {
428: map.put(key, values);
429: }
430: }
431:
432: return map;
433: }
434: }
|