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.wcs.servlets;
006:
007: import org.geoserver.ows.util.EncodingInfo;
008: import org.geoserver.ows.util.XmlCharsetDetector;
009: import org.vfny.geoserver.ServiceException;
010: import org.vfny.geoserver.global.GeoServer;
011: import org.vfny.geoserver.servlets.AbstractService;
012: import org.vfny.geoserver.servlets.Dispatcher;
013: import org.vfny.geoserver.util.requests.readers.DispatcherKvpReader;
014: import org.vfny.geoserver.util.requests.readers.DispatcherXmlReader;
015: import org.vfny.geoserver.util.requests.readers.KvpRequestReader;
016: import org.vfny.geoserver.wcs.WcsException;
017: import java.io.BufferedInputStream;
018: import java.io.BufferedOutputStream;
019: import java.io.BufferedReader;
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.FileOutputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.Reader;
026: import java.util.Map;
027: import java.util.logging.Level;
028: import java.util.logging.Logger;
029: import java.util.regex.Pattern;
030: import javax.servlet.ServletContext;
031: import javax.servlet.ServletException;
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034: import javax.servlet.http.HttpSession;
035:
036: /**
037: * Routes requests made at the top-level URI to appropriate interface servlet.
038: * Note that the logic of this method could be generously described as
039: * 'loose.' It is not checking for request validity in any way (this is done
040: * by the reqeust- specific servlets). Rather, it is attempting to make a
041: * reasonable guess as to what servlet to call, given that the client is
042: * routing to the top level URI as opposed to the request-specific URI, as
043: * specified in the GetCapabilities response. Thus, this is a convenience
044: * method, which allows for some slight client laziness and helps explain to
045: * lost souls/spiders what lives at the URL. Due to the string parsing, it is
046: * much faster (and recommended) to use the URIs specified in the
047: * GetCapabablities response. Currently does not support post requests, but
048: * most requests for this will likely come with get.
049: *
050: * @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last modification)
051: * @author $Author: Simone Giannecchini (simboss1@gmail.com) $ (last modification)
052: * @version $Id: WcsDispatcher.java 7746 2007-11-13 15:38:35Z aaime $
053: */
054: public class WcsDispatcher extends Dispatcher {
055: /**
056: * Comment for <code>serialVersionUID</code>
057: */
058: private static final long serialVersionUID = 3977857384599203894L;
059:
060: /** Class logger */
061: private static Logger LOGGER = org.geotools.util.logging.Logging
062: .getLogger("org.vfny.geoserver.servlets.wcs");
063: private static int sequence = 123;
064: private static final String DEFAULT_ENCODING = "UTF-8";
065: private static final String ENCODING_HEADER_ARG = "Content-Type";
066: private static final Pattern ENCODING_PATTERN = Pattern
067: .compile("encoding\\s*\\=\\s*\"([^\"]+)\"");
068:
069: /** Temporary file used to store the request */
070: private File temp;
071:
072: /**
073: * This figures out a dispatched post request. It writes the request to a
074: * temp file, and then reads it in twice from there. I found no other way
075: * to do this, but this solution doesn't seem to bad. Obviously it is
076: * better to use the servlets directly, without having to go through this
077: * random file writing and reading. If our xml handlers were written more
078: * dynamically this probably wouldn't be necessary, but it is. Hopefully
079: * this should help GeoServer's interoperability with clients, since most
080: * of them are kinda stupid, and can't handle different url locations for
081: * the different requests.
082: *
083: * @param request The servlet request object.
084: * @param response The servlet response object.
085: *
086: * @throws ServletException For any servlet problems.
087: * @throws IOException For any io problems.
088: */
089: public void doPost(HttpServletRequest request,
090: HttpServletResponse response) throws ServletException,
091: IOException {
092: int targetRequest = 0;
093:
094: try {
095: InputStream is = new BufferedInputStream(request
096: .getInputStream());
097:
098: // REVISIT: Should do more than sequence here
099: // (In case we are running two GeoServers at once)
100: // - Could we use response.getHandle() in the filename?
101: // - ProcessID is traditional, I don't know how to find that in Java
102: sequence++;
103: temp = File.createTempFile("wcsdispatch" + sequence, "tmp");
104:
105: FileOutputStream fos = new FileOutputStream(temp);
106: BufferedOutputStream out = new BufferedOutputStream(fos);
107:
108: int c;
109:
110: while (-1 != (c = is.read())) {
111: out.write(c);
112: }
113:
114: is.close();
115: out.flush();
116: out.close();
117:
118: //JD: GEOS-323, Adding char encoding support
119: EncodingInfo encInfo = new EncodingInfo();
120:
121: BufferedReader disReader;
122: BufferedReader requestReader;
123:
124: try {
125: disReader = new BufferedReader(XmlCharsetDetector
126: .getCharsetAwareReader(
127: new FileInputStream(temp), encInfo));
128:
129: requestReader = new BufferedReader(XmlCharsetDetector
130: .createReader(new FileInputStream(temp),
131: encInfo));
132: } catch (Exception e) {
133: /*
134: * Any exception other than WcsException will "hang up" the
135: * process - no client output, no log entries, only "Internal
136: * server error". So this is a little trick to make detector's
137: * exceptions "visible".
138: */
139: throw new WcsException(e);
140: }
141:
142: //
143: // String req_enc = guessRequestEncoding(request);
144: // BufferedReader disReader = new BufferedReader(
145: // new InputStreamReader(
146: // new FileInputStream(temp), req_enc));
147: //
148: // BufferedReader requestReader = new BufferedReader(
149: // new InputStreamReader(
150: // new FileInputStream(temp), req_enc));
151:
152: //JD: GEOS-323, Adding char encoding support
153: if (disReader != null) {
154: DispatcherXmlReader requestTypeAnalyzer = new DispatcherXmlReader();
155:
156: try {
157: requestTypeAnalyzer.read(disReader, request);
158: } catch (ServiceException e) {
159: throw new WcsException(e);
160: }
161:
162: //targetRequest = requestTypeAnalyzer.getRequestType();
163: } else {
164: targetRequest = UNKNOWN;
165: }
166:
167: LOGGER.fine("post got request " + targetRequest);
168:
169: doResponse(requestReader, request, response, targetRequest);
170: } catch (ServiceException wcs) {
171: HttpSession session = request.getSession();
172: ServletContext context = session.getServletContext();
173: GeoServer geoServer = (GeoServer) context
174: .getAttribute(GeoServer.WEB_CONTAINER_KEY);
175: String tempResponse = ((WcsException) wcs)
176: .getXmlResponse(geoServer.isVerboseExceptions(),
177: request, geoServer);
178:
179: response.setContentType(geoServer.getCharSet().toString());
180: response.getWriter().write(tempResponse);
181: }
182: }
183:
184: /**
185: * Handles all Get requests. This method implements the main matching
186: * logic for the class.
187: *
188: * @param request The servlet request object.
189: * @param response The servlet response object.
190: *
191: * @throws ServletException For any servlet problems.
192: * @throws IOException For any io problems.
193: */
194: public void doGet(HttpServletRequest request,
195: HttpServletResponse response) throws ServletException,
196: IOException {
197: int targetRequest = 0;
198:
199: // Examine the incoming request and create appropriate server objects
200: // to deal with each request
201: // try {
202: final String queryString = request.getQueryString();
203:
204: if (queryString != null) {
205: Map kvPairs = KvpRequestReader.parseKvpSet(queryString);
206: targetRequest = DispatcherKvpReader.getRequestType(kvPairs);
207: } else {
208: targetRequest = UNKNOWN;
209:
210: //throw exception
211: }
212:
213: doResponse(null, request, response, targetRequest);
214: }
215:
216: /**
217: * Does the actual response, creates the appropriate servlet to handle the
218: * detected request. Note that the requestReader is a bit of a hack, if
219: * it is null then it is from a get Request, if not then that means the
220: * request is being stored as a file, and needs to be read with this
221: * particular reader.
222: *
223: * <p>
224: * This does have the downside of forcing us to have doGet and doPost
225: * methods of AbstractService be public, perhaps there is a good pattern
226: * for handling this. Or we could try to re-write Dispatcher to extend
227: * AbstractService, but it may be tricky.
228: * </p>
229: *
230: * @param requestReader The reader of a file that contains the request,
231: * null if from a get request.
232: * @param request The http request that was made.
233: * @param response The response to be returned.
234: * @param req_type The request type.
235: *
236: * @throws ServletException for any problems.
237: * @throws IOException If anything goes wrong reading or writing.
238: */
239: protected void doResponse(Reader requestReader,
240: HttpServletRequest request, HttpServletResponse response,
241: int req_type) throws ServletException, IOException {
242: AbstractService dispatched;
243:
244: if (LOGGER.isLoggable(Level.INFO)) {
245: LOGGER.info(new StringBuffer("req_type is ").append(
246: req_type).toString());
247: }
248:
249: // switch (req_type) {
250: // case GET_CAPABILITIES_REQUEST:
251: // dispatched = new Capabilities();
252: //
253: // break;
254: //
255: // case DESCRIBE_COVERAGE_REQUEST:
256: // dispatched = new Describe();
257: //
258: // break;
259: //
260: // case GET_COVERAGE_REQUEST:
261: // dispatched = new Coverage();
262: //
263: // break;
264: //
265: // default:
266: // dispatched = null;
267: // }
268: //
269: // if ((dispatched != null)) {
270: // dispatched.init(servletConfig); //only really needed for init
271: //
272: // if (requestReader == null) {
273: // dispatched.doGet(request, response);
274: // } else {
275: // dispatched.doPost(request, response, requestReader);
276: // }
277: // } else {
278: // String message;
279: //
280: // if (requestReader == null) {
281: // message = "No request recognized. The REQUEST parameter"
282: // + " must be one of GetCoverage or DescribeCoverage.";
283: // } else {
284: // message = "The proper request could not be extracted from the"
285: // + "the xml posted. Make sure the case is correct. The "
286: // + "request must be one of GetCoverage or DescribeCoverage.";
287: // }
288: // HttpSession session = request.getSession();
289: // ServletContext context = session.getServletContext();
290: // GeoServer geoServer = (GeoServer) context.getAttribute(GeoServer.WEB_CONTAINER_KEY);
291: //
292: // WcsException wcse = new WcsException(message);
293: // String tempResponse = wcse.getXmlResponse(geoServer.isVerboseExceptions(), request);
294: //
295: // response.setContentType(geoServer.getCharSet().toString());
296: // response.getWriter().write(tempResponse);
297: // }
298: }
299:
300: // /**
301: // * Gets the request encoding by taking a couple guesses. First it tries
302: // * to get the encoding specified in the actual XML sent, which is likely
303: // * the most accurate. We are willing to take the speed hit to be more
304: // * sure of the right encoding. If that is not present we take a shot
305: // * at the encoding used to send the http request. If that is not found
306: // * then we just go ahead with the default encoding. Thanks to Artie Konin
307: // * for this work, it's right on.
308: // *
309: // * @param request The http request object to guess the encoding of.
310: // * @return A string of the best guess of the encoding of the request.
311: // */
312: // protected String guessRequestEncoding(HttpServletRequest request) {
313: // String defaultEncoding = DEFAULT_ENCODING;
314: // String encoding = getXmlEncoding();
315: // if (encoding == null) {
316: // encoding = request.getHeader(ENCODING_HEADER_ARG);
317: // if (encoding == null) {
318: // encoding = defaultEncoding;
319: // } else {
320: // if (encoding.indexOf("=") == -1) {
321: // encoding = defaultEncoding;
322: // } else {
323: // int encodingIndex = encoding.lastIndexOf("=") + 1;
324: // encoding = encoding.substring(encodingIndex).trim();
325: // }
326: // }
327: // }
328: // return encoding;
329: // }
330: //
331: // /**
332: // * Gets the encoding of the xml request made to the dispatcher. This
333: // * works by reading the temp file where we are storing the request,
334: // * looking to match the header specified encoding that should be present
335: // * on all xml files. This call should only be made after the temp file
336: // * has been set. If no encoding is found, or if an IOError is encountered
337: // * then null shall be returned.
338: // *
339: // * @return The encoding specified in the xml header of the file stored
340: // * in 'temp'.
341: // */
342: // protected String getXmlEncoding() {
343: // try {
344: // StringWriter sw = new StringWriter(60);
345: // BufferedReader in = new BufferedReader(new FileReader(temp));
346: //
347: // int c;
348: // while ((-1 != (c = in.read())) && (0x3E != c)) {
349: // sw.write(c);
350: // }
351: // in.close();
352: //
353: // Matcher m = ENCODING_PATTERN.matcher(sw.toString());
354: // if (m.find()) {
355: // String result = m.group(1);
356: // LOGGER.info("got match: " + result);
357: // return result;
358: // //return m.toMatchResult().group(1);
359: // } else {
360: // return null;
361: // }
362: // } catch (IOException e) {
363: // return null;
364: // }
365: // }
366: }
|