0001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
0002: * This code is licensed under the GPL 2.0 license, availible at the root
0003: * application directory.
0004: */
0005: package org.vfny.geoserver.servlets;
0006:
0007: import org.geoserver.ows.ServiceStrategy;
0008: import org.geoserver.ows.util.XmlCharsetDetector;
0009: import org.springframework.beans.BeansException;
0010: import org.springframework.context.ApplicationContext;
0011: import org.springframework.context.ApplicationContextAware;
0012: import org.springframework.web.context.WebApplicationContext;
0013: import org.vfny.geoserver.ExceptionHandler;
0014: import org.vfny.geoserver.Request;
0015: import org.vfny.geoserver.Response;
0016: import org.vfny.geoserver.ServiceException;
0017: import org.vfny.geoserver.global.Data;
0018: import org.vfny.geoserver.global.GeoServer;
0019: import org.vfny.geoserver.global.Service;
0020: import org.vfny.geoserver.util.PartialBufferedOutputStream2;
0021: import org.vfny.geoserver.util.requests.readers.KvpRequestReader;
0022: import org.vfny.geoserver.util.requests.readers.XmlRequestReader;
0023: import java.io.BufferedOutputStream;
0024: import java.io.BufferedReader;
0025: import java.io.IOException;
0026: import java.io.OutputStream;
0027: import java.io.Reader;
0028: import java.net.SocketException;
0029: import java.nio.charset.Charset;
0030: import java.util.Enumeration;
0031: import java.util.HashMap;
0032: import java.util.Iterator;
0033: import java.util.Map;
0034: import java.util.logging.Level;
0035: import java.util.logging.Logger;
0036: import javax.servlet.ServletContext;
0037: import javax.servlet.ServletException;
0038: import javax.servlet.http.HttpServlet;
0039: import javax.servlet.http.HttpServletRequest;
0040: import javax.servlet.http.HttpServletResponse;
0041:
0042: /**
0043: * Represents a service that all others extend from. Subclasses should provide
0044: * response and exception handlers as appropriate.
0045: *
0046: * <p>
0047: * It is <b>really</b> important to adhere to the following workflow:
0048: *
0049: * <ol>
0050: * <li>
0051: * get a Request reader
0052: * </li>
0053: * <li>
0054: * ask the Request Reader for the Request object
0055: * </li>
0056: * <li>
0057: * Provide the resulting Request with the ServletRequest that generated it
0058: * </li>
0059: * <li>
0060: * get the appropiate ResponseHandler
0061: * </li>
0062: * <li>
0063: * ask it to execute the Request
0064: * </li>
0065: * <li>
0066: * set the response content type
0067: * </li>
0068: * <li>
0069: * write to the http response's output stream
0070: * </li>
0071: * <li>
0072: * pending - call Response cleanup
0073: * </li>
0074: * </ol>
0075: * </p>
0076: *
0077: * <p>
0078: * If anything goes wrong a ServiceException can be thrown and will be written
0079: * to the output stream instead.
0080: * </p>
0081: *
0082: * <p>
0083: * This is because we have to be sure that no exception have been produced
0084: * before setting the response's content type, so we can set the exception
0085: * specific content type; and that Response.getContentType is called AFTER
0086: * Response.execute, since the MIME type can depend on any request parameter
0087: * or another kind of desission making during the execute process. (i.e.
0088: * FORMAT in WMS GetMap)
0089: * </p>
0090: *
0091: * <p>
0092: * TODO: We need to call Response.abort() if anything goes wrong to allow the
0093: * Response a chance to cleanup after itself.
0094: * </p>
0095: *
0096: * @author Gabriel Rold?n
0097: * @author Chris Holmes
0098: * @author Jody Garnett, Refractions Research
0099: * @version $Id: AbstractService.java 8443 2008-02-25 13:18:59Z groldan $
0100: */
0101: public abstract class AbstractService extends HttpServlet implements
0102: ApplicationContextAware {
0103: /** Class logger */
0104: protected static Logger LOGGER = org.geotools.util.logging.Logging
0105: .getLogger("org.vfny.geoserver.servlets");
0106:
0107: /**
0108: * Servivce group (maps to 'SERVICE' parameter in OGC service urls)
0109: */
0110: String service;
0111:
0112: /**
0113: * Request type (maps to 'REQUEST' parameter in OGC service urls)
0114: */
0115: String request;
0116:
0117: /**
0118: * Application context used to look up "Services"
0119: */
0120: WebApplicationContext context;
0121:
0122: /**
0123: * Reference to the global geoserver instnace.
0124: */
0125: GeoServer geoServer;
0126:
0127: /**
0128: * Reference to the catalog.
0129: */
0130: Data catalog;
0131:
0132: /**
0133: * Id of the service strategy to use.
0134: */
0135: String serviceStrategy;
0136:
0137: /**
0138: * buffer size to use when PARTIAL-BUFFER is being used
0139: */
0140: int partialBufferSize;
0141:
0142: /**
0143: * Cached service strategy object
0144: */
0145:
0146: // ServiceStrategy strategy;
0147: /**
0148: * Reference to the service
0149: */
0150: Service serviceRef;
0151: private String kvpString;
0152:
0153: // /** DOCUMENT ME! */
0154: // protected HttpServletRequest curRequest;
0155:
0156: /**
0157: * Constructor for abstract service.
0158: *
0159: * @param service The service group the service falls into (WFS,WMS,...)
0160: * @param request The service being requested (GetCapabilities, GetMap, ...)
0161: * @param serviceRef The global service this "servlet" falls into
0162: */
0163: public AbstractService(String service, String request,
0164: Service serviceRef) {
0165: this .service = service;
0166: this .request = request;
0167: this .serviceRef = serviceRef;
0168: }
0169:
0170: /**
0171: * @return Returns the "service group" that this service falls into.
0172: */
0173: public String getService() {
0174: return service;
0175: }
0176:
0177: /**
0178: * @return Returns the "request" this service maps to.
0179: */
0180: public String getRequest() {
0181: return request;
0182: }
0183:
0184: /**
0185: * Sets a refeference to the global service instance.
0186: */
0187: public void setServiceRef(Service serviceRef) {
0188: this .serviceRef = serviceRef;
0189: }
0190:
0191: /**
0192: * @return The reference to the global service instance.
0193: */
0194: public Service getServiceRef() {
0195: return serviceRef;
0196: }
0197:
0198: /**
0199: * Sets the application context.
0200: * <p>
0201: * Used to process the {@link Service} extension point.
0202: * </p>
0203: */
0204: public void setApplicationContext(ApplicationContext context)
0205: throws BeansException {
0206: this .context = (WebApplicationContext) context;
0207: }
0208:
0209: /**
0210: * @return The application context.
0211: */
0212: public WebApplicationContext getApplicationContext() {
0213: return context;
0214: }
0215:
0216: /**
0217: * Sets the reference to the global geoserver instance.
0218: */
0219: public void setGeoServer(GeoServer geoServer) {
0220: this .geoServer = geoServer;
0221: }
0222:
0223: /**
0224: * @return the reference to the global geoserver instance.
0225: */
0226: public GeoServer getGeoServer() {
0227: return geoServer;
0228: }
0229:
0230: /**
0231: * @return The reference to the global catalog instance.
0232: */
0233: public Data getCatalog() {
0234: return catalog;
0235: }
0236:
0237: /**
0238: * Sets the reference to the global catalog instance.
0239: *
0240: */
0241: public void setCatalog(Data catalog) {
0242: this .catalog = catalog;
0243: }
0244:
0245: /**
0246: * @return The id used to identify the service strategy to be used.
0247: * @see ServiceStrategy#getId()
0248: */
0249: public String getServiceStrategy() {
0250: return serviceStrategy;
0251: }
0252:
0253: /**
0254: * Sets the id used to identify the service strategy to be used.
0255: */
0256: public void setServiceStrategy(String serviceStrategy) {
0257: this .serviceStrategy = serviceStrategy;
0258: }
0259:
0260: /**
0261: * Determines if the service is enabled.
0262: * <p>
0263: * Subclass should override this method if the service can be turned on/off.
0264: * This implementation returns <code>true</code>
0265: * </p>
0266: */
0267: protected boolean isServiceEnabled(HttpServletRequest req) {
0268: return true;
0269: }
0270:
0271: /**
0272: * Override and use spring set servlet context.
0273: */
0274: public ServletContext getServletContext() {
0275: //override and use spring
0276: return ((WebApplicationContext) context).getServletContext();
0277: }
0278:
0279: /**
0280: * DOCUMENT ME!
0281: *
0282: * @param request DOCUMENT ME!
0283: * @param response DOCUMENT ME!
0284: *
0285: * @throws ServletException DOCUMENT ME!
0286: * @throws IOException DOCUMENT ME!
0287: */
0288: public void doGet(HttpServletRequest request,
0289: HttpServletResponse response) throws ServletException,
0290: IOException {
0291: // implements the main request/response logic
0292: // this.curRequest = request;
0293: Request serviceRequest = null;
0294:
0295: if (!isServiceEnabled(request)) {
0296: sendDisabledServiceError(response);
0297:
0298: return;
0299: }
0300:
0301: try {
0302: Map requestParams = new HashMap();
0303: String qString = ((this .kvpString != null) ? this .kvpString
0304: : request.getQueryString());
0305: LOGGER.fine("reading request: " + qString);
0306:
0307: if (this .kvpString != null) {
0308: requestParams = KvpRequestReader.parseKvpSet(qString);
0309: } else {
0310: String paramName;
0311: String paramValue;
0312:
0313: for (Enumeration pnames = request.getParameterNames(); pnames
0314: .hasMoreElements();) {
0315: paramName = (String) pnames.nextElement();
0316: paramValue = request.getParameter(paramName);
0317: requestParams.put(paramName.toUpperCase(),
0318: paramValue);
0319: }
0320: }
0321:
0322: KvpRequestReader requestReader = getKvpReader(requestParams);
0323:
0324: serviceRequest = requestReader.getRequest(request);
0325: LOGGER
0326: .finer("serviceRequest provided with HttpServletRequest: "
0327: + request);
0328:
0329: //serviceRequest.setHttpServletRequest(request);
0330: } catch (ServiceException se) {
0331: sendError(request, response, se);
0332:
0333: return;
0334: } catch (Throwable e) {
0335: sendError(request, response, e);
0336:
0337: return;
0338: } finally {
0339: this .kvpString = null;
0340: }
0341:
0342: doService(request, response, serviceRequest);
0343: }
0344:
0345: /**
0346: * Sends the standard disabled service error message (a 503 error followed by an english description).
0347: * @param response
0348: * @throws IOException
0349: */
0350: protected void sendDisabledServiceError(HttpServletResponse response)
0351: throws IOException {
0352: response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
0353: getService() + " service is not enabled. "
0354: + "You can enable it in the web admin tool.");
0355: }
0356:
0357: /**
0358: * Performs the post method. Simply passes itself on to the three argument
0359: * doPost method, with null for the reader, because the
0360: * request.getReader() will not have been used if this servlet is called
0361: * directly.
0362: *
0363: * @param request DOCUMENT ME!
0364: * @param response DOCUMENT ME!
0365: *
0366: * @throws ServletException DOCUMENT ME!
0367: * @throws IOException DOCUMENT ME!
0368: */
0369: public void doPost(HttpServletRequest request,
0370: HttpServletResponse response) throws ServletException,
0371: IOException {
0372: doPost(request, response, null);
0373: }
0374:
0375: /**
0376: * Performs the post method. Gets the appropriate xml reader and
0377: * determines the request from that, and then passes the request on to
0378: * doService.
0379: *
0380: * @param request The request made.
0381: * @param response The response to be returned.
0382: * @param requestXml A reader of the xml to be read. This is only used by
0383: * the dispatcher, everyone else should just pass in null. This is
0384: * needed because afaik HttpServletRequest.getReader() can not be
0385: * used twice. So in a dispatched case we write it to a temp file,
0386: * which we can then read in twice.
0387: *
0388: * @throws ServletException DOCUMENT ME!
0389: * @throws IOException DOCUMENT ME!
0390: */
0391: public void doPost(HttpServletRequest request,
0392: HttpServletResponse response, Reader requestXml)
0393: throws ServletException, IOException {
0394: // this.curRequest = request;
0395: Request serviceRequest = null;
0396:
0397: //TODO: This isn't a proper ogc service response.
0398: if (!isServiceEnabled(request)) {
0399: sendDisabledServiceError(response);
0400:
0401: return;
0402: }
0403:
0404: // implements the main request/response logic
0405: try {
0406: XmlRequestReader requestReader = getXmlRequestReader();
0407:
0408: //JD: GEOS-323, adding support for character encoding detection
0409: // Reader xml = (requestXml != null) ? requestXml : request.getReader();
0410: Reader xml;
0411:
0412: if (null != requestXml) {
0413: xml = requestXml;
0414: } else {
0415: /*
0416: * `getCharsetAwareReader` returns a reader which not support
0417: * mark/reset. So it is a good idea to wrap it into BufferedReader.
0418: * In this case the below debug output will work.
0419: */
0420: xml = new BufferedReader(
0421: XmlCharsetDetector
0422: .getCharsetAwareReader(request
0423: .getInputStream()));
0424: }
0425:
0426: //JD: GEOS-323
0427:
0428: //DJB: add support for POST loggin
0429: if (LOGGER.isLoggable(Level.FINE)) {
0430: if (xml.markSupported()) {
0431: // a little protection for large POSTs (ie. updates)
0432: // for FINE, I assume people just want to see the "normal" ones - not the big ones
0433: // for FINER, I assume they would want to see a bit more
0434: // for FINEST, I assume they would want to see even more
0435: int maxChars = 16000;
0436:
0437: if (LOGGER.isLoggable(Level.FINER)) {
0438: maxChars = 64000;
0439: }
0440:
0441: if (LOGGER.isLoggable(Level.FINEST)) {
0442: maxChars = 640000; // Bill gates says 640k is good enough for anyone
0443: }
0444:
0445: xml.mark(maxChars + 1); // +1 so if you read the whole thing you can still reset()
0446:
0447: char[] buffer = new char[maxChars];
0448: int actualRead = xml.read(buffer);
0449: xml.reset();
0450: LOGGER
0451: .fine("------------XML POST START-----------\n"
0452: + new String(buffer, 0, actualRead)
0453: + "\n------------XML POST END-----------");
0454:
0455: if (actualRead == maxChars) {
0456: LOGGER
0457: .fine("------------XML POST REPORT WAS TRUNCATED AT "
0458: + maxChars
0459: + " CHARACTERS. RUN WITH HIGHER LOGGING LEVEL TO SEE MORE");
0460: }
0461: } else {
0462: LOGGER
0463: .fine("ATTEMPTED TO LOG POST XML, BUT WAS PREVENTED BECAUSE markSupported() IS FALSE");
0464: }
0465: }
0466:
0467: serviceRequest = requestReader.read(xml, request);
0468: serviceRequest.setHttpServletRequest(request);
0469: } catch (ServiceException se) {
0470: sendError(request, response, se);
0471:
0472: return;
0473: } catch (Throwable e) {
0474: sendError(request, response, e);
0475:
0476: return;
0477: }
0478:
0479: doService(request, response, serviceRequest);
0480: }
0481:
0482: /**
0483: * Peforms service according to ServiceStrategy.
0484: *
0485: * <p>
0486: * This method has very strict requirements, please see the class
0487: * description for the specifics.
0488: * </p>
0489: *
0490: * <p>
0491: * It has a lot of try/catch blocks, but they are fairly necessary to
0492: * handle things correctly and to avoid as many ugly servlet responses, so
0493: * that everything is wrapped correctly.
0494: * </p>
0495: *
0496: * @param request The httpServlet of the request.
0497: * @param response The response to be returned.
0498: * @param serviceRequest The OGC request to service.
0499: *
0500: * @throws ServletException if the strategy can't be instantiated
0501: */
0502: protected void doService(HttpServletRequest request,
0503: HttpServletResponse response, Request serviceRequest)
0504: throws ServletException {
0505: LOGGER.info("handling request: " + serviceRequest);
0506:
0507: if (!isServiceEnabled(request)) {
0508: try {
0509: sendDisabledServiceError(response);
0510: } catch (IOException e) {
0511: LOGGER
0512: .log(
0513: Level.WARNING,
0514: "Error writing service unavailable response",
0515: e);
0516: }
0517:
0518: return;
0519: }
0520:
0521: ServiceStrategy strategy = null;
0522: Response serviceResponse = null;
0523:
0524: try {
0525: strategy = createServiceStrategy();
0526: LOGGER.fine("strategy is: " + strategy.getId());
0527: serviceResponse = getResponseHandler();
0528: } catch (Throwable t) {
0529: sendError(request, response, t);
0530:
0531: return;
0532: }
0533:
0534: Map services = context.getBeansOfType(Service.class);
0535: Service s = null;
0536:
0537: for (Iterator itr = services.entrySet().iterator(); itr
0538: .hasNext();) {
0539: Map.Entry entry = (Map.Entry) itr.next();
0540: String id = (String) entry.getKey();
0541: Service service = (Service) entry.getValue();
0542:
0543: if (id.toLowerCase().startsWith(
0544: serviceRequest.getService().toLowerCase().trim())) {
0545: s = service;
0546:
0547: break;
0548: }
0549: }
0550:
0551: if (s == null) {
0552: String msg = "No service found matching: "
0553: + serviceRequest.getService();
0554: sendError(request, response, new ServiceException(msg));
0555:
0556: return;
0557: }
0558:
0559: try {
0560: // execute request
0561: LOGGER.finer("executing request");
0562: serviceResponse.execute(serviceRequest);
0563: LOGGER.finer("execution succeed");
0564: } catch (ServiceException serviceException) {
0565: LOGGER
0566: .warning("service exception while executing request: "
0567: + serviceRequest
0568: + "\ncause: "
0569: + serviceException.getMessage());
0570: serviceResponse.abort(s);
0571: sendError(request, response, serviceException);
0572:
0573: return;
0574: } catch (Throwable t) {
0575: //we can safelly send errors here, since we have not touched response yet
0576: serviceResponse.abort(s);
0577: sendError(request, response, t);
0578:
0579: return;
0580: }
0581:
0582: OutputStream strategyOuput = null;
0583:
0584: //obtain the strategy output stream
0585: try {
0586: LOGGER.finest("getting strategy output");
0587: strategyOuput = strategy.getDestination(response);
0588: LOGGER.finer("strategy output is: "
0589: + strategyOuput.getClass().getName());
0590:
0591: String mimeType = serviceResponse.getContentType(s
0592: .getGeoServer());
0593: LOGGER.fine("mime type is: " + mimeType);
0594: response.setContentType(mimeType);
0595:
0596: String encoding = serviceResponse.getContentEncoding();
0597:
0598: if (encoding != null) {
0599: LOGGER.fine("content encoding is: " + encoding);
0600: response.setHeader("Content-Encoding", encoding);
0601: }
0602:
0603: String disposition = serviceResponse
0604: .getContentDisposition();
0605:
0606: if (disposition != null) {
0607: LOGGER.fine("content encoding is: " + encoding);
0608: response.setHeader("Content-Disposition", disposition);
0609: }
0610: } catch (SocketException socketException) {
0611: LOGGER
0612: .fine("it seems that the user has closed the request stream: "
0613: + socketException.getMessage());
0614:
0615: // It seems the user has closed the request stream
0616: // Apparently this is a "cancel" and will quietly go away
0617: //
0618: // I will still give strategy and serviceResponse
0619: // a chance to clean up
0620: //
0621: serviceResponse.abort(s);
0622: strategy.abort();
0623:
0624: return;
0625: } catch (IOException ex) {
0626: serviceResponse.abort(s);
0627: strategy.abort();
0628: sendError(request, response, ex);
0629:
0630: return;
0631: }
0632:
0633: try {
0634: // gather response
0635: serviceResponse.writeTo(strategyOuput);
0636: strategyOuput.flush();
0637: strategy.flush(response);
0638: } catch (java.net.SocketException sockEx) { // user cancel
0639: LOGGER
0640: .info("Stream abruptly closed by client, response aborted");
0641: serviceResponse.abort(s);
0642: strategy.abort();
0643:
0644: return;
0645: } catch (IOException ioException) { // strategyOutput error
0646: response.setHeader("Content-Disposition", ""); // reset it so we get a proper XML error returned
0647: LOGGER
0648: .info("Stream abruptly closed by client, response aborted");
0649: LOGGER.log(Level.FINE, "Error writing out "
0650: + ioException.getMessage(), ioException);
0651: serviceResponse.abort(s);
0652: strategy.abort();
0653:
0654: return;
0655: } catch (ServiceException writeToFailure) { // writeTo Failure
0656: response.setHeader("Content-Disposition", ""); // reset it so we get a proper XML error returned
0657: serviceResponse.abort(s);
0658: strategy.abort();
0659: sendError(request, response, writeToFailure);
0660:
0661: return;
0662: } catch (Throwable help) { // This is an unexpected error(!)
0663: response.setHeader("Content-Disposition", ""); // reset it so we get a proper XML error returned
0664: help.printStackTrace();
0665: serviceResponse.abort(s);
0666: strategy.abort();
0667: sendError(request, response, help);
0668:
0669: return;
0670: }
0671:
0672: // Finish Response
0673: // I have moved closing the output stream out here, it was being
0674: // done by a few of the ServiceStrategy
0675: //
0676: // By this time serviceResponse has finished successfully
0677: // and strategy is also finished
0678: //
0679: try {
0680: response.getOutputStream().flush();
0681: response.getOutputStream().close();
0682: } catch (SocketException sockEx) { // user cancel
0683: LOGGER.warning("Could not send completed response to user:"
0684: + sockEx);
0685:
0686: return;
0687: } catch (IOException ioException) {
0688: // This is bad, the user did not get the completed response
0689: LOGGER.warning("Could not send completed response to user:"
0690: + ioException);
0691:
0692: return;
0693: }
0694:
0695: LOGGER.info("Service handled");
0696: }
0697:
0698: /**
0699: * Gets the response class that should handle the request of this service.
0700: * All subclasses must implement.
0701: * <p>
0702: * This method is not abstract to support subclasses that use the
0703: * request-response mechanism.
0704: * </p>
0705: *
0706: * @return The response that the request read by this servlet should be
0707: * passed to.
0708: */
0709: protected Response getResponseHandler() {
0710: return null;
0711: }
0712:
0713: /**
0714: * This method was added in order to adapt the old style servlet services
0715: * to the new ows dispatching interface, without having to modify the
0716: * services themselves.
0717: *
0718: * @return A call to {@link #getResponseHandler()}.
0719: */
0720: public final Response getResponse() {
0721: return getResponseHandler();
0722: }
0723:
0724: /**
0725: * Gets a reader that will figure out the correct Key Vaule Pairs for this
0726: * service.
0727: * <p>
0728: * Subclasses should override to supply a specific kvp reader. Default
0729: * implementation returns <code>null</code>
0730: * </p>
0731: * @param params A map of the kvp pairs.
0732: *
0733: * @return An initialized KVP reader to decode the request.
0734: */
0735: protected KvpRequestReader getKvpReader(Map params) {
0736: return null;
0737: }
0738:
0739: /**
0740: * Gets a reader that will handle a posted xml request for this servlet.
0741: * <p>
0742: * Subclasses should override to supply a specific xml reader. Default
0743: * implementation returns <code>null</code>
0744: * </p>
0745: * @return An XmlRequestReader appropriate to this service.
0746: */
0747: protected XmlRequestReader getXmlRequestReader() {
0748: return null;
0749: }
0750:
0751: /**
0752: * Gets the exception handler for this service.
0753: *
0754: * @return The correct ExceptionHandler
0755: */
0756: protected abstract ExceptionHandler getExceptionHandler();
0757:
0758: /**
0759: * Gets the strategy for outputting the response. This method gets the
0760: * strategy from the serviceStrategy param in the web.xml file. This is
0761: * sort of odd behavior, as all other such parameters are set in the
0762: * services and catalog xml files, and this param may move there. But as
0763: * it is much more of a programmer configuration than a user
0764: * configuration there is no rush to move it.
0765: *
0766: * <p>
0767: * Subclasses may choose to override this method in order to get a strategy
0768: * more suited to their response. Currently only Transaction will do
0769: * this, since the commit is only called after writeTo, and it often
0770: * messes up, so we want to be able to see the error message (SPEED writes
0771: * the output directly, so errors in writeTo do not show up.)
0772: * </p>
0773: *
0774: * <p>
0775: * Most subclasses should not override, this method will most always return
0776: * the SPEED strategy, since it is the fastest response and should work
0777: * fine if everything is well tested. FILE and BUFFER should be used when
0778: * there are errors in writeTo methods of child classes, set by the
0779: * programmer in the web.xml file.
0780: * </p>
0781: *
0782: * @return The service strategy found in the web.xml serviceStrategy
0783: * parameter. The code that finds this is in the init method
0784: *
0785: * @throws ServiceException If the service strategy set in #init() is not
0786: * valid.
0787: *
0788: * @see #init() for the code that sets the serviceStrategy.
0789: */
0790: protected ServiceStrategy createServiceStrategy()
0791: throws ServiceException {
0792: // If verbose exceptions is on then lets make sure they actually get the
0793: // exception by using the file strategy.
0794: ServiceStrategy theStrategy = null;
0795:
0796: if (geoServer.isVerboseExceptions()) {
0797: theStrategy = (ServiceStrategy) context
0798: .getBean("fileServiceStrategy");
0799: } else {
0800: if (serviceStrategy == null) {
0801: // none set, look up in web applicatino context
0802: serviceStrategy = getServletContext().getInitParameter(
0803: "serviceStrategy");
0804: }
0805:
0806: // do a lookup
0807: if (serviceStrategy != null) {
0808: Map strategies = context
0809: .getBeansOfType(ServiceStrategy.class);
0810:
0811: for (Iterator itr = strategies.values().iterator(); itr
0812: .hasNext();) {
0813: ServiceStrategy bean = (ServiceStrategy) itr.next();
0814:
0815: if (bean.getId().equals(serviceStrategy)) {
0816: theStrategy = bean;
0817:
0818: break;
0819: }
0820: }
0821: }
0822: }
0823:
0824: if (theStrategy == null) {
0825: // default to buffer
0826: theStrategy = (ServiceStrategy) context
0827: .getBean("bufferServiceStrategy");
0828: }
0829:
0830: // clone the strategy since at the moment the strategies are marked as singletons
0831: // in the web.xml file.
0832: try {
0833: theStrategy = (ServiceStrategy) theStrategy.clone();
0834: } catch (CloneNotSupportedException e) {
0835: LOGGER.log(Level.SEVERE,
0836: "Programming error found, service strategies should be cloneable, "
0837: + e, e);
0838: throw new RuntimeException(
0839: "Found a strategy that does not support cloning...",
0840: e);
0841: }
0842:
0843: // TODO: this hack should be removed once modules have their own config
0844: if (theStrategy instanceof PartialBufferStrategy2) {
0845: if (partialBufferSize == 0) {
0846: String size = getServletContext().getInitParameter(
0847: "PARTIAL_BUFFER_STRATEGY_SIZE");
0848:
0849: if (size != null) {
0850: try {
0851: partialBufferSize = Integer.valueOf(size)
0852: .intValue();
0853:
0854: if (partialBufferSize <= 0) {
0855: LOGGER
0856: .warning("Invalid partial buffer size, defaulting to "
0857: + PartialBufferedOutputStream2.DEFAULT_BUFFER_SIZE
0858: + " (was "
0859: + partialBufferSize + ")");
0860: partialBufferSize = 0;
0861: }
0862: } catch (NumberFormatException nfe) {
0863: LOGGER
0864: .warning("Invalid partial buffer size, defaulting to "
0865: + PartialBufferedOutputStream2.DEFAULT_BUFFER_SIZE
0866: + " (was "
0867: + partialBufferSize
0868: + ")");
0869: partialBufferSize = 0;
0870: }
0871: }
0872: }
0873:
0874: ((PartialBufferStrategy2) theStrategy)
0875: .setBufferSize(partialBufferSize);
0876: }
0877:
0878: return theStrategy;
0879: }
0880:
0881: /**
0882: * DOCUMENT ME!
0883: *
0884: * @return DOCUMENT ME!
0885: */
0886: protected String getMimeType() {
0887: ServletContext servContext = getServletContext();
0888:
0889: try {
0890: return ((GeoServer) servContext.getAttribute("GeoServer"))
0891: .getMimeType();
0892: } catch (NullPointerException e) {
0893: return "text/xml; charset="
0894: + Charset.forName("UTF-8").name();
0895: }
0896: }
0897:
0898: /**
0899: * DOCUMENT ME!
0900: *
0901: * @param response DOCUMENT ME!
0902: * @param content DOCUMENT ME!
0903: */
0904: protected void send(HttpServletResponse response,
0905: CharSequence content) {
0906: send(response, content, getMimeType());
0907: }
0908:
0909: /**
0910: * DOCUMENT ME!
0911: *
0912: * @param response DOCUMENT ME!
0913: * @param content DOCUMENT ME!
0914: * @param mimeType DOCUMENT ME!
0915: */
0916: protected void send(HttpServletResponse response,
0917: CharSequence content, String mimeType) {
0918: try {
0919: response.setContentType(mimeType);
0920: response.getWriter().write(content.toString());
0921: } catch (IOException ex) { //stream closed by client, do nothing
0922: LOGGER
0923: .info("Stream abruptly closed by client, response aborted");
0924: LOGGER.fine(ex.getMessage());
0925: } catch (IllegalStateException ex) { //stream closed by client, do nothing
0926: LOGGER
0927: .info("Stream abruptly closed by client, response aborted");
0928: LOGGER.fine(ex.getMessage());
0929: }
0930: }
0931:
0932: /**
0933: * Send error produced during getService opperation.
0934: *
0935: * <p>
0936: * Some errors know how to write themselves out WfsTransactionException for
0937: * instance. It looks like this might be is handled by
0938: * getExceptionHandler().newServiceException( t, pre, null ). I still
0939: * would not mind seeing a check for ServiceConfig Exception here.
0940: * </p>
0941: *
0942: * <p>
0943: * This code says that it deals with UNCAUGHT EXCEPTIONS, so I think it
0944: * would be wise to explicitly catch ServiceExceptions.
0945: * </p>
0946: *
0947: * @param response DOCUMENT ME!
0948: * @param t DOCUMENT ME!
0949: */
0950: protected void sendError(HttpServletRequest request,
0951: HttpServletResponse response, Throwable t) {
0952: if (t instanceof ServiceException) {
0953: sendError(request, response, (ServiceException) t);
0954:
0955: return;
0956: }
0957:
0958: LOGGER.info("Had an undefined error: " + t.getMessage());
0959:
0960: //TODO: put the stack trace in the logger.
0961: //t.printStackTrace();
0962: //String pre = "UNCAUGHT EXCEPTION";
0963: ExceptionHandler exHandler = getExceptionHandler();
0964: ServiceException se = exHandler.newServiceException(t);
0965:
0966: sendError(request, response, se);
0967:
0968: //GeoServer geoServer = (GeoServer) this.getServletConfig()
0969: // .getServletContext().getAttribute(GeoServer.WEB_CONTAINER_KEY);
0970: //send(response, se.getXmlResponse(geoServer.isVerboseExceptions()));
0971: }
0972:
0973: /**
0974: * Send a serviceException produced during getService opperation.
0975: *
0976: * @param response DOCUMENT ME!
0977: * @param se DOCUMENT ME!
0978: */
0979: protected void sendError(HttpServletRequest request,
0980: HttpServletResponse response, ServiceException se) {
0981: // first log the exception
0982: LOGGER.log(Level.SEVERE, "Service exception occurred", se);
0983:
0984: String mimeType = se.getMimeType(geoServer);
0985:
0986: send(response, se.getXmlResponse(geoServer
0987: .isVerboseExceptions(), request, geoServer), mimeType);
0988: }
0989:
0990: /**
0991: * DOCUMENT ME!
0992: *
0993: * @param response DOCUMENT ME!
0994: * @param result DOCUMENT ME!
0995: */
0996: protected void send(HttpServletRequest httpRequest,
0997: HttpServletResponse response, Response result) {
0998: OutputStream responseOut = null;
0999:
1000: try {
1001: responseOut = response.getOutputStream();
1002: } catch (IOException ex) { //stream closed, do nothing.
1003: LOGGER.info("apparently client has closed stream: "
1004: + ex.getMessage());
1005: }
1006:
1007: OutputStream out = new BufferedOutputStream(responseOut);
1008: ServletContext servContext = getServletContext();
1009: response.setContentType(result
1010: .getContentType((GeoServer) servContext
1011: .getAttribute("GeoServer")));
1012:
1013: try {
1014: result.writeTo(out);
1015: out.flush();
1016: responseOut.flush();
1017: } catch (IOException ioe) {
1018: //user just closed the socket stream, do nothing
1019: LOGGER.fine("connection closed by user: "
1020: + ioe.getMessage());
1021: } catch (ServiceException ex) {
1022: sendError(httpRequest, response, ex);
1023: }
1024: }
1025:
1026: /**
1027: * Checks if the client requests supports gzipped responses by quering it's
1028: * 'accept-encoding' header.
1029: *
1030: * @param request the request to query the HTTP header from
1031: *
1032: * @return true if 'gzip' if one of the supported content encodings of
1033: * <code>request</code>, false otherwise.
1034: */
1035: protected boolean requestSupportsGzip(HttpServletRequest request) {
1036: boolean supportsGzip = false;
1037: String header = request.getHeader("accept-encoding");
1038:
1039: if ((header != null) && (header.indexOf("gzip") > -1)) {
1040: supportsGzip = true;
1041: }
1042:
1043: if (LOGGER.isLoggable(Level.CONFIG)) {
1044: LOGGER.config("user-agent="
1045: + request.getHeader("user-agent"));
1046: LOGGER.config("accept=" + request.getHeader("accept"));
1047: LOGGER.config("accept-encoding="
1048: + request.getHeader("accept-encoding"));
1049: }
1050:
1051: return supportsGzip;
1052: }
1053:
1054: public String getKvpString() {
1055: return kvpString;
1056: }
1057:
1058: public void setKvpString(String kvpString) {
1059: this.kvpString = kvpString;
1060: }
1061: }
|