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.geoserver.test;
006:
007: import java.io.BufferedReader;
008: import java.io.ByteArrayInputStream;
009: import java.io.IOException;
010: import java.io.InputStream;
011: import java.io.InputStreamReader;
012: import java.util.Collection;
013: import java.util.Iterator;
014: import java.util.StringTokenizer;
015: import java.util.logging.Level;
016: import java.util.logging.Logger;
017:
018: import javax.xml.namespace.QName;
019: import javax.xml.parsers.DocumentBuilder;
020: import javax.xml.parsers.DocumentBuilderFactory;
021: import javax.xml.transform.OutputKeys;
022: import javax.xml.transform.Transformer;
023: import javax.xml.transform.TransformerFactory;
024: import javax.xml.transform.dom.DOMSource;
025: import javax.xml.transform.stream.StreamResult;
026:
027: import junit.framework.TestCase;
028:
029: import org.apache.log4j.LogManager;
030: import org.geoserver.data.test.MockData;
031: import org.geoserver.ows.Dispatcher;
032: import org.geoserver.ows.util.ResponseUtils;
033: import org.geoserver.platform.GeoServerExtensions;
034: import org.geoserver.platform.GeoServerResourceLoader;
035: import org.geotools.data.FeatureSource;
036: import org.geotools.factory.Hints;
037: import org.geotools.util.logging.Log4JLoggerFactory;
038: import org.geotools.util.logging.Logging;
039: import org.springframework.web.servlet.HandlerInterceptor;
040: import org.vfny.geoserver.global.Data;
041: import org.vfny.geoserver.global.GeoServer;
042: import org.vfny.geoserver.global.GeoserverDataDirectory;
043: import org.w3c.dom.Document;
044: import org.w3c.dom.Element;
045: import org.w3c.dom.NodeList;
046:
047: import com.mockrunner.mock.web.MockHttpServletRequest;
048: import com.mockrunner.mock.web.MockHttpServletResponse;
049: import com.mockrunner.mock.web.MockHttpSession;
050: import com.mockrunner.mock.web.MockServletContext;
051:
052: /**
053: * Base test class for GeoServer unit tests.
054: * <p>
055: * Deriving from this test class provides the test case with preconfigured
056: * geoserver and catalog objects.
057: * </p>
058: * <p>
059: * This test case provides a spring application context which loads the
060: * application contexts from all modules on the classpath.
061: * </p>
062: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
063: */
064: public class GeoServerTestSupport extends TestCase {
065: /**
066: * Common logger for test cases
067: */
068: protected static final Logger LOGGER = org.geotools.util.logging.Logging
069: .getLogger("org.geoserver.test");
070:
071: /**
072: * mock GeoServer data directory
073: */
074: protected MockData dataDirectory;
075:
076: /**
077: * Application context
078: */
079: protected GeoServerTestApplicationContext applicationContext;
080:
081: /**
082: * If subclasses overide they *must* call super.setUp() first.
083: */
084: protected void setUp() throws Exception {
085: super .setUp();
086:
087: // configure axis ordering
088: Hints.putSystemDefault(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,
089: Boolean.TRUE);
090: Hints.putSystemDefault(Hints.FORCE_AXIS_ORDER_HONORING, "http");
091:
092: //set up the data directory
093: dataDirectory = new MockData();
094: dataDirectory.setUp();
095:
096: // setup quiet logging (we need to to this here because Data
097: // is loaded before GoeServer has a chance to setup logging for good)
098: try {
099: Logging.ALL.setLoggerFactory(Log4JLoggerFactory
100: .getInstance());
101: } catch (Exception e) {
102: LOGGER.log(Level.SEVERE,
103: "Could not configure log4j logging redirection", e);
104: }
105: GeoServer.suppressLoggingConfiguration();
106: setupLogging(getClass().getResourceAsStream(
107: getDefaultLogConfiguration()));
108:
109: //copy the service configuration to the data directory
110: dataDirectory.copyTo(GeoServerTestSupport.class
111: .getResourceAsStream("services.xml"), "services.xml");
112:
113: //set up a mock servlet context
114: MockServletContext servletContext = new MockServletContext();
115: servletContext.setInitParameter("GEOSERVER_DATA_DIR",
116: dataDirectory.getDataDirectoryRoot().getAbsolutePath());
117: servletContext.setInitParameter("serviceStrategy",
118: "PARTIAL-BUFFER2");
119:
120: applicationContext = new GeoServerTestApplicationContext(
121: getSpringContextLocations(), servletContext);
122:
123: applicationContext.refresh();
124: }
125:
126: /**
127: * Returns the logging configuration path. The default value is "/TEST_LOGGING.properties", which
128: * is a pretty quiet configuration. Should you need more verbose logging override this method
129: * in subclasses and choose a different configuration, for example "/DEFAULT_LOGGING.properties".
130: * @return
131: */
132: protected String getDefaultLogConfiguration() {
133: return "/TEST_LOGGING.properties";
134: }
135:
136: protected void setupLogging(InputStream loggingConfigStream)
137: throws Exception {
138: GeoServer.configureGeoServerLogging(loggingConfigStream, false,
139: true, null);
140: }
141:
142: /**
143: * Returns the spring context locations to be used in order to build the GeoServer Spring
144: * context. Subclasses might want to provide extra locations in order to test extension points.
145: * @return
146: */
147: protected String[] getSpringContextLocations() {
148: return new String[] { "classpath*:/applicationContext.xml",
149: "classpath*:/applicationSecurityContext.xml" };
150: }
151:
152: /**
153: * If subclasses overide they *must* call super.tearDown() first.
154: */
155: protected void tearDown() throws Exception {
156: super .tearDown();
157:
158: //kill the context
159: applicationContext.destroy();
160: applicationContext = null;
161:
162: //kill the data directory
163: dataDirectory.tearDown();
164: GeoserverDataDirectory.destroy();
165: dataDirectory = null;
166: }
167:
168: /**
169: * Accessor for global catalog instance from the test application context.
170: */
171: protected Data getCatalog() {
172: return (Data) applicationContext.getBean("data");
173: }
174:
175: /**
176: * Accessor for global geoserver instance from the test application context.
177: */
178: protected GeoServer getGeoServer() {
179: return (GeoServer) applicationContext.getBean("geoServer");
180: }
181:
182: /**
183: * Accessor for global resource loader instance from the test application context.
184: */
185: protected GeoServerResourceLoader getResourceLoader() {
186: return (GeoServerResourceLoader) applicationContext
187: .getBean("resourceLoader");
188: }
189:
190: /**
191: * Loads a feature source from the catalog.
192: *
193: * @param typeName The qualified type name of the feature source.
194: */
195: protected FeatureSource getFeatureSource(QName typeName)
196: throws IOException {
197: return getCatalog().getFeatureSource(typeName.getPrefix(),
198: typeName.getLocalPart());
199: }
200:
201: /**
202: * Convenience method for subclasses to create mock http servlet requests.
203: * <p>
204: * Examples of using this method are:
205: * <pre>
206: * <code>
207: * createRequest( "wfs?request=GetCapabilities" ); //get
208: * createRequest( "wfs" ); //post
209: * </code>
210: * </pre>
211: * </p>
212: * @param path The path for the request and optional the query string.
213: * @return
214: */
215: protected MockHttpServletRequest createRequest(String path) {
216: MockHttpServletRequest request = new MockHttpServletRequest();
217:
218: request.setScheme("http");
219: request.setServerName("localhost");
220: request.setContextPath("/geoserver");
221: request.setRequestURI(ResponseUtils
222: .stripQueryString(ResponseUtils.appendPath(
223: "/geoserver/", path)));
224: request.setQueryString(ResponseUtils.stripQueryString(path));
225: request.setRemoteAddr("127.0.0.1");
226: request.setServletPath(path);
227: kvp(request, path);
228:
229: MockHttpSession session = new MockHttpSession();
230: session.setupServletContext(new MockServletContext());
231: request.setSession(session);
232:
233: request.setUserPrincipal(null);
234:
235: return request;
236: }
237:
238: /**
239: * Executes an ows request using the GET method.
240: *
241: * @param path The porition of the request after hte context,
242: * example: 'wms?request=GetMap&version=1.1.1&..."
243: *
244: * @return An input stream which is the result of the request.
245: *
246: * @throws Exception
247: */
248: protected InputStream get(String path) throws Exception {
249: MockHttpServletResponse response = getAsServletResponse(path);
250: return new ByteArrayInputStream(response
251: .getOutputStreamContent().getBytes());
252: }
253:
254: /**
255: * Executes an ows request using the GET method.
256: *
257: * @param path The porition of the request after hte context,
258: * example: 'wms?request=GetMap&version=1.1.1&..."
259: *
260: * @return the mock servlet response
261: *
262: * @throws Exception
263: */
264: protected MockHttpServletResponse getAsServletResponse(String path)
265: throws Exception {
266: MockHttpServletRequest request = createRequest(path);
267: request.setMethod("GET");
268: request.setBodyContent(new byte[] {});
269:
270: return dispatch(request);
271: }
272:
273: /**
274: * Executes an ows request using the POST method with key value pairs
275: * form encoded.
276: *
277: * @param path The porition of the request after hte context,
278: * example: 'wms?request=GetMap&version=1.1.1&..."
279: *
280: * @return An input stream which is the result of the request.
281: *
282: * @throws Exception
283: */
284: protected InputStream post(String path) throws Exception {
285: MockHttpServletRequest request = createRequest(path);
286: request.setMethod("POST");
287: request.setContentType("application/x-www-form-urlencoded");
288:
289: MockHttpServletResponse response = dispatch(request);
290: return new ByteArrayInputStream(response
291: .getOutputStreamContent().getBytes());
292: }
293:
294: /**
295: * Executes an ows request using the POST method.
296: * <p>
297: *
298: * </p>
299: * @param path The porition of the request after the context ( no query string ),
300: * example: 'wms'.
301: *
302: * @return An input stream which is the result of the request.
303: *
304: * @throws Exception
305: */
306: protected InputStream post(String path, String xml)
307: throws Exception {
308: MockHttpServletRequest request = createRequest(path);
309: request.setMethod("POST");
310: request.setContentType("application/xml");
311: request.setBodyContent(xml);
312:
313: MockHttpServletResponse response = dispatch(request);
314: return new ByteArrayInputStream(response
315: .getOutputStreamContent().getBytes());
316: }
317:
318: /**
319: * Executes an ows request using the GET method and returns the result as an
320: * xml document.
321: *
322: * @param path The porition of the request after hte context,
323: * example: 'wms?request=GetMap&version=1.1.1&..."
324: *
325: * @return A result of the request parsed into a dom.
326: *
327: * @throws Exception
328: */
329: protected Document getAsDOM(String path) throws Exception {
330: return dom(get(path));
331: }
332:
333: /**
334: * Executes an ows request using the POST method with key value pairs
335: * form encoded, returning the result as a dom.
336: *
337: * @param path The porition of the request after hte context,
338: * example: 'wms?request=GetMap&version=1.1.1&..."
339: *
340: * @return An input stream which is the result of the request.
341: *
342: * @throws Exception
343: */
344: protected Document postAsDOM(String path) throws Exception {
345: return dom(post(path));
346: }
347:
348: /**
349: * Executes an ows request using the POST method and returns the result as an
350: * xml document.
351: * <p>
352: *
353: * </p>
354: * @param path The porition of the request after the context ( no query string ),
355: * example: 'wms'.
356: *
357: * @return An input stream which is the result of the request.
358: *
359: * @throws Exception
360: */
361: protected Document postAsDOM(String path, String xml)
362: throws Exception {
363: return dom(post(path, xml));
364: }
365:
366: protected String getAsString(String path) throws Exception {
367: return string(get(path));
368: }
369:
370: /**
371: * Parses a stream into a dom.
372: */
373: protected Document dom(InputStream input) throws Exception {
374: DocumentBuilderFactory factory = DocumentBuilderFactory
375: .newInstance();
376: factory.setNamespaceAware(true);
377: //factory.setValidating( true );
378:
379: DocumentBuilder builder = factory.newDocumentBuilder();
380: Document dom = builder.parse(input);
381:
382: return dom;
383: }
384:
385: /**
386: * Parses a stream into a String
387: */
388: protected String string(InputStream input) throws Exception {
389: BufferedReader reader = null;
390: StringBuffer sb = new StringBuffer();
391: char[] buf = new char[8192];
392: try {
393: reader = new BufferedReader(new InputStreamReader(input));
394: String line = null;
395: while ((line = reader.readLine()) != null)
396: sb.append(line);
397: } finally {
398: if (reader != null)
399: reader.close();
400: }
401: return sb.toString();
402: }
403:
404: /**
405: * Utility method to print out a dom.
406: */
407: protected void print(Document dom) throws Exception {
408: TransformerFactory txFactory = TransformerFactory.newInstance();
409:
410: Transformer tx = txFactory.newTransformer();
411: tx.setOutputProperty(OutputKeys.INDENT, "yes");
412:
413: tx.transform(new DOMSource(dom), new StreamResult(System.out));
414: }
415:
416: /**
417: * Convenience method for element.getElementsByTagName() to return the
418: * first element in the resulting node list.
419: */
420: protected Element getFirstElementByTagName(Element element,
421: String name) {
422: NodeList elements = element.getElementsByTagName(name);
423: if (elements.getLength() > 0) {
424: return (Element) elements.item(0);
425: }
426:
427: return null;
428: }
429:
430: /**
431: * Convenience method for element.getElementsByTagName() to return the
432: * first element in the resulting node list.
433: */
434: protected Element getFirstElementByTagName(Document dom, String name) {
435: return getFirstElementByTagName(dom.getDocumentElement(), name);
436: }
437:
438: /**
439: * Sets up a template in a feature type directory.
440: *
441: * @param featureTypeName The name of the feature type.
442: * @param template The name of the template.
443: * @param body The content of the template.
444: *
445: * @throws IOException
446: */
447: protected void setupTemplate(QName featureTypeName,
448: String template, String body) throws IOException {
449:
450: dataDirectory.copyToFeatureTypeDirectory(
451: new ByteArrayInputStream(body.getBytes()),
452: featureTypeName, template);
453: }
454:
455: /*
456: * Helper method to create the kvp params from the query string.
457: */
458: private void kvp(MockHttpServletRequest request, String path) {
459: int index = path.indexOf('?');
460:
461: if (index == -1) {
462: return;
463: }
464:
465: String queryString = path.substring(index + 1);
466: StringTokenizer st = new StringTokenizer(queryString, "&");
467:
468: while (st.hasMoreTokens()) {
469: String token = st.nextToken();
470: String[] keyValuePair = token.split("=");
471:
472: //check for any special characters
473: if (keyValuePair.length > 1) {
474: //replace any equals or & characters
475: keyValuePair[1] = keyValuePair[1]
476: .replaceAll("%3D", "=");
477: keyValuePair[1] = keyValuePair[1]
478: .replaceAll("%3d", "=");
479: keyValuePair[1] = keyValuePair[1]
480: .replaceAll("%23", "&");
481: }
482: request.setupAddParameter(keyValuePair[0],
483: keyValuePair.length > 1 ? keyValuePair[1] : "");
484: }
485: }
486:
487: /*
488: * Helper method for dispatching an executing an ows request.
489: */
490: private MockHttpServletResponse dispatch(
491: MockHttpServletRequest request) throws Exception {
492: //create the response
493: //final MockServletOutputStream output = new MockServletOutputStream();
494: MockHttpServletResponse response = new MockHttpServletResponse() {
495: public void setCharacterEncoding(String encoding) {
496:
497: }
498: // public ServletOutputStream getOutputStream() throws IOException {
499: // return output;
500: // }
501: };
502:
503: //look up the handler
504: Dispatcher dispatcher = (Dispatcher) applicationContext
505: .getBean("dispatcher");
506: //dispatcher.setApplicationContext( getGeoServer().getApplicationContext() );
507:
508: //excute the pre handler step
509: Collection interceptors = GeoServerExtensions.extensions(
510: HandlerInterceptor.class, applicationContext);
511: for (Iterator i = interceptors.iterator(); i.hasNext();) {
512: HandlerInterceptor interceptor = (HandlerInterceptor) i
513: .next();
514: interceptor.preHandle(request, response, dispatcher);
515: }
516:
517: //execute
518: dispatcher.handleRequest(request, response);
519:
520: //execute the post handler step
521: for (Iterator i = interceptors.iterator(); i.hasNext();) {
522: HandlerInterceptor interceptor = (HandlerInterceptor) i
523: .next();
524: interceptor.postHandle(request, response, dispatcher, null);
525: }
526:
527: return response;
528: }
529:
530: }
|