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.testdata;
006:
007: import com.mockrunner.mock.web.MockHttpServletRequest;
008: import com.mockrunner.mock.web.MockHttpSession;
009: import com.mockrunner.mock.web.MockServletContext;
010: import org.geoserver.wfs.WFS;
011: import org.geotools.data.DataSourceException;
012: import org.geotools.data.DataStore;
013: import org.geotools.data.DataUtilities;
014: import org.geotools.data.property.PropertyDataStore;
015: import org.geotools.feature.FeatureType;
016: import org.geotools.feature.SchemaException;
017: import org.geotools.referencing.crs.DefaultGeographicCRS;
018: import org.vfny.geoserver.global.ConfigurationException;
019: import org.vfny.geoserver.global.Data;
020: import org.vfny.geoserver.global.GeoServer;
021: import org.vfny.geoserver.global.WMS;
022: import org.vfny.geoserver.global.dto.DataDTO;
023: import org.vfny.geoserver.global.dto.DataStoreInfoDTO;
024: import org.vfny.geoserver.global.dto.FeatureTypeInfoDTO;
025: import org.vfny.geoserver.global.dto.NameSpaceInfoDTO;
026: import org.vfny.geoserver.global.dto.ServiceDTO;
027: import org.vfny.geoserver.global.dto.StyleDTO;
028: import org.vfny.geoserver.global.dto.WFSDTO;
029: import org.vfny.geoserver.global.dto.WMSDTO;
030: import java.io.File;
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.OutputStream;
034: import java.net.URL;
035: import java.util.Arrays;
036: import java.util.Collections;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.Map;
040:
041: /**
042: * Provides utility methods for the creation of mock objects to help in unit
043: * testing specific geoserver classes.
044: * <p>
045: * Of interest is the {@linkplain #newHttpRequest(Map, boolean)} method which
046: * provides a fully functional mocked up GeoServer instance configured with a
047: * PropertyDataStore holding CITE FeatureTypes and convenient styles, etc.
048: * </p>
049: * <p>
050: * The PropertyDataStore is created against a temporary directory so it is safe
051: * to make transactions on each test. A further improvement would be to create
052: * just a MemoryDataStore, but it was easier to reuse the code in
053: * AbstractCiteDataTest in the while.
054: * </p>
055: *
056: * @author Gabriel Roldan
057: *
058: */
059: public class MockUtils {
060: private MockUtils() {
061: }
062:
063: /**
064: *
065: * @param includeMockGeoserver
066: * if <code>true</code>, the generated HttpServletRequest
067: * includes a mock geoserver configuration for the cite test
068: * data, so you can use it, for example, for unit testing request
069: * readers.
070: * @return
071: */
072: public static MockHttpServletRequest newHttpRequest(
073: boolean includeMockGeoserver)
074: throws ConfigurationException, IOException {
075: return newHttpRequest(Collections.EMPTY_MAP,
076: includeMockGeoserver);
077: }
078:
079: /**
080: * Creates a mock HttpServletRequest with the provided set of request
081: * parameters, and possibly a fully configured GeoServer with cite test data
082: * on the request's HttpServletContext.
083: *
084: * @param initialParams
085: * a map of request parameters to construct the mock http request
086: * with, where keys are parameter names, and values may be a
087: * single String or a String[] if there are multiple values for
088: * the same request parameter.
089: * @param includeMockGeoserver
090: * if <code>true</code>, the generated HttpServletRequest
091: * includes a mock geoserver configuration for the cite test
092: * data, so you can use it, for example, for unit testing request
093: * readers.
094: * @return
095: */
096: public static MockHttpServletRequest newHttpRequest(
097: Map /* <String, String> */initialParams,
098: boolean includeMockGeoServer)
099: throws ConfigurationException, IOException {
100: MockHttpServletRequest request = new MockHttpServletRequest();
101:
102: for (Iterator it = initialParams.entrySet().iterator(); it
103: .hasNext();) {
104: Map.Entry e = (Map.Entry) it.next();
105:
106: String name = (String) e.getKey();
107: Object value = e.getValue();
108:
109: if ((value != null) && value.getClass().isArray()) {
110: if (value.getClass().getComponentType() != String.class) {
111: throw new IllegalArgumentException(
112: "provided an illegal parameter for " + name
113: + ": " + value);
114: }
115:
116: request.setupAddParameter(name, (String[]) value);
117: } else {
118: request.setupAddParameter(name, (String) value);
119: }
120: }
121:
122: if (includeMockGeoServer) {
123: setUpMockGeoServer(request);
124: }
125:
126: return request;
127: }
128:
129: /**
130: * * public static WMS getWMS(HttpServletRequest request) { ServletRequest
131: * req = request; HttpSession session = request.getSession(); ServletContext
132: * context = session.getServletContext();
133: *
134: * return (WMS) context.getAttribute(WMS.WEB_CONTAINER_KEY); }
135: *
136: * @param request
137: */
138: private static void setUpMockGeoServer(
139: MockHttpServletRequest request)
140: throws ConfigurationException, IOException {
141: final GeoServer mockGeoServer = new GeoServer();
142: final Data citeData = createTestCiteData(mockGeoServer);
143: WMSDTO wmsDto = newWmsDto();
144: WMS wms = new WMS(wmsDto) {
145: public Data getData() {
146: return citeData;
147: }
148: };
149:
150: WFSDTO wfsDto = newWfsDto();
151: WFS wfs = new WFS(wfsDto) {
152: public Data getData() {
153: return citeData;
154: }
155: };
156:
157: MockHttpSession session = new MockHttpSession();
158: MockServletContext ctx = new MockServletContext();
159:
160: ctx.setAttribute(WMS.WEB_CONTAINER_KEY, wms);
161: ctx.setAttribute(WFS.WEB_CONTAINER_KEY, wfs);
162: ctx.setAttribute(GeoServer.WEB_CONTAINER_KEY, mockGeoServer);
163:
164: session.setupServletContext(ctx);
165: request.setSession(session);
166: }
167:
168: /**
169: * Creates a new mock WMS config object.
170: * <p>
171: * The creates WMS config object has no data (i.e. getData() returns null)
172: * </p>
173: *
174: * @return
175: */
176: public static WMSDTO newWmsDto() {
177: WMSDTO dto = new WMSDTO();
178: dto.setGmlPrefixing(true);
179:
180: ServiceDTO service = new ServiceDTO();
181: service.setAbstract("test abstract");
182: service.setAccessConstraints("NONE");
183: service.setEnabled(true);
184: service.setFees("NONE");
185: service.setKeywords(Arrays.asList(new String[] { "test",
186: "mock", "service", "config" }));
187: service.setMaintainer("Gabriel Roldan");
188: service.setName("WMS");
189:
190: try {
191: service.setOnlineResource(new URL("http://www.axios.es"));
192: } catch (Exception e) {
193: // no-op
194: }
195:
196: service.setTitle("My mock WMS");
197: dto.setService(service);
198:
199: return dto;
200: }
201:
202: /**
203: * Creates a new mock WMS config object.
204: * <p>
205: * The creates WMS config object has no data (i.e. getData() returns null)
206: * </p>
207: *
208: * @return
209: */
210: public static WFSDTO newWfsDto() {
211: WFSDTO dto = new WFSDTO();
212: dto.setCiteConformanceHacks(true);
213:
214: ServiceDTO service = new ServiceDTO();
215: service.setAbstract("test abstract");
216: service.setAccessConstraints("NONE");
217: service.setEnabled(true);
218: service.setFees("NONE");
219: service.setKeywords(Arrays.asList(new String[] { "test",
220: "mock", "service", "config" }));
221: service.setMaintainer("Gabriel Roldan");
222: service.setName("WMS");
223:
224: try {
225: service.setOnlineResource(new URL("http://www.axios.es"));
226: } catch (Exception e) {
227: // no-op
228: }
229:
230: service.setTitle("My mock WMS");
231: dto.setService(service);
232: dto.setServiceLevel(WFSDTO.COMPLETE);
233:
234: return dto;
235: }
236:
237: /**
238: *
239: * @param geoserver
240: * @return
241: * @throws ConfigurationException
242: * @throws IOException
243: */
244: public static Data createTestCiteData(GeoServer geoserver)
245: throws ConfigurationException, IOException {
246: DataDTO dataDto = new DataDTO();
247: File dir = null;
248:
249: URL testDataUrl = MockUtils.class.getResource("test-data");
250: System.out.println(testDataUrl);
251:
252: if (!"file".equals(testDataUrl.getProtocol())) {
253: throw new IOException("unsupported protocol: "
254: + testDataUrl.getProtocol());
255: }
256:
257: String url = testDataUrl.toExternalForm();
258: String testPath = url.substring("file:".length());
259: dir = new File(testPath);
260:
261: if (!dir.exists() || !dir.isDirectory()) {
262: throw new ConfigurationException(
263: "Expected cite test dataset directory at " + dir);
264: }
265:
266: Map formats = new HashMap();
267: dataDto.setFormats(formats);
268:
269: Map coverages = new HashMap();
270: dataDto.setCoverages(coverages);
271:
272: Map dataStores = createDataStoresMap();
273: dataDto.setDataStores(dataStores);
274:
275: Map featureTypes = createFeatureTypes();
276: dataDto.setFeaturesTypes(featureTypes);
277:
278: Map nameSpaces = createNameSpaces();
279: dataDto.setNameSpaces(nameSpaces);
280: dataDto.setDefaultNameSpacePrefix("cite");
281:
282: Map styles = createStyles(new File(dir, "styles"));
283: dataDto.setStyles(styles);
284:
285: Data catalog = new Data(dataDto, dir, geoserver);
286:
287: return catalog;
288: }
289:
290: private static Map createDataStoresMap() throws IOException {
291: Map map = new HashMap();
292: DataStoreInfoDTO dsDto = new DataStoreInfoDTO();
293: dsDto.setAbstract("test cite data for unit testing geoserver");
294: dsDto.setEnabled(true);
295: dsDto.setId("cite");
296: dsDto.setNameSpaceId("cite");
297: dsDto.setTitle("same as abstract");
298:
299: final File envTmpDir = new File(System
300: .getProperty("java.io.tmpdir"));
301: File tempDir = new File(envTmpDir, "cite_test_datastore");
302: createCiteDataStore(tempDir);
303:
304: Map dsConnectionParams = new HashMap();
305: dsConnectionParams.put("directory", tempDir);
306: dsDto.setConnectionParams(dsConnectionParams);
307:
308: map.put("cite", dsDto);
309:
310: return map;
311: }
312:
313: private static Map createFeatureTypes() {
314: Map map = new HashMap();
315:
316: FeatureTypeInfoDTO ftDto;
317:
318: for (int i = 0; i < AbstractCiteDataTest.CITE_TYPE_NAMES.length; i++) {
319: String typeName = AbstractCiteDataTest.CITE_TYPE_NAMES[i];
320: ftDto = new FeatureTypeInfoDTO();
321: ftDto.setAbstract(typeName + " abstract");
322: ftDto.setDataStoreId("cite");
323: ftDto.setDefaultStyle(typeName);
324: ftDto.setDirName(null);
325: ftDto.setName(typeName);
326: ftDto.setSRS(4326);
327: ftDto.setTitle("title for " + typeName);
328:
329: map.put(typeName, ftDto);
330: }
331:
332: return map;
333: }
334:
335: private static Map createNameSpaces() {
336: Map map = new HashMap();
337: NameSpaceInfoDTO ns = new NameSpaceInfoDTO();
338: ns.setDefault(true);
339: ns.setPrefix("cite");
340: ns.setUri("http://www.axios.es");
341: map.put("cite", ns);
342:
343: return map;
344: }
345:
346: private static Map createStyles(File baseDir) {
347: Map map = new HashMap();
348:
349: StyleDTO dto = new StyleDTO();
350: dto.setDefault(false);
351: dto.setFilename(new File(baseDir, "default.sld"));
352: dto.setId("default");
353: map.put("default", dto);
354:
355: for (int i = 0; i < AbstractCiteDataTest.CITE_TYPE_NAMES.length; i++) {
356: String typeName = AbstractCiteDataTest.CITE_TYPE_NAMES[i];
357: String sldName = typeName + ".sld";
358: File sldFile = new File(baseDir, sldName);
359:
360: if (!sldFile.exists()) {
361: System.err
362: .println("Style file not found, unsing default.sld: "
363: + sldFile);
364:
365: continue;
366: }
367:
368: dto = new StyleDTO();
369: dto.setDefault(false);
370: dto.setFilename(sldFile);
371: dto.setId(typeName);
372: map.put(typeName, dto);
373: }
374:
375: return map;
376: }
377:
378: /**
379: * Returns a <code>DataStore</code> containing CITE feature types.
380: *
381: * @return a property files backed DataStore which forces all the
382: * FeatureTypes it serves to be in WGS84 CRS.
383: *
384: * @throws IOException
385: * DOCUMENT ME!
386: */
387: public static DataStore createCiteDataStore(File tempDir)
388: throws IOException {
389: writeTempFiles(tempDir);
390:
391: DataStore propsDS = new ForceWGS84PropertyDataStore(tempDir);
392:
393: return propsDS;
394: }
395:
396: /**
397: * DOCUMENT ME!
398: *
399: * @throws IOException
400: * DOCUMENT ME!
401: */
402: private static void writeTempFiles(File tempDir) throws IOException {
403: if (tempDir.exists()) {
404: tempDir.delete();
405: }
406:
407: tempDir.mkdir();
408:
409: if (!tempDir.exists() || !tempDir.isDirectory()) {
410: throw new IOException(tempDir.getAbsolutePath()
411: + " is not a writable directory");
412: }
413:
414: for (int i = 0; i < AbstractCiteDataTest.CITE_TYPE_NAMES.length; i++) {
415: writeTempFile(tempDir,
416: AbstractCiteDataTest.CITE_TYPE_NAMES[i]);
417: }
418: tempDir.deleteOnExit();
419: }
420:
421: /**
422: * DOCUMENT ME!
423: *
424: * @param typeName
425: * DOCUMENT ME!
426: *
427: * @throws IOException
428: * DOCUMENT ME!
429: * @throws NullPointerException
430: * DOCUMENT ME!
431: */
432: private static void writeTempFile(File tempDir,
433: final String typeName) throws IOException {
434: final String fileName = typeName + ".properties";
435:
436: File outFile = new File(tempDir, fileName);
437:
438: // perhaps it was not deleted in a previous, broken run...
439: deleteTempFile(tempDir, typeName);
440:
441: // Atomically creates a new, empty file named by this abstract
442: // pathname if and only if a file with this name does not yet exist.
443: outFile.createNewFile();
444:
445: // Request that the file or directory denoted by this abstract
446: // pathname be deleted when the virtual machine terminates.
447: outFile.deleteOnExit();
448:
449: String resourceName = "test-data/featureTypes/" + fileName;
450:
451: InputStream in = MockUtils.class
452: .getResourceAsStream(resourceName);
453:
454: if (in == null) {
455: throw new NullPointerException(resourceName
456: + " not found in classpath");
457: }
458:
459: OutputStream out = new java.io.FileOutputStream(outFile);
460: byte[] buff = new byte[512];
461: int count;
462:
463: while ((count = in.read(buff)) > -1) {
464: out.write(buff, 0, count);
465: }
466:
467: in.close();
468: out.flush();
469: out.close();
470: }
471:
472: /**
473: * DOCUMENT ME!
474: *
475: * @param typeName
476: * DOCUMENT ME!
477: */
478: private static void deleteTempFile(File tempDir, String typeName) {
479: deleteTempFile(new File(tempDir, typeName + ".properties"));
480: }
481:
482: /**
483: * DOCUMENT ME!
484: *
485: * @param f
486: * DOCUMENT ME!
487: */
488: private static void deleteTempFile(File f) {
489: if (f.exists() && !f.delete())
490: throw new RuntimeException("Could not delete file " + f);
491: }
492:
493: /**
494: * DOCUMENT ME!
495: *
496: * @author Gabriel Roldan, Axios Engineering
497: * @version $Id: MockUtils.java 7349 2007-08-02 11:06:37Z aaime $
498: */
499: private static class ForceWGS84PropertyDataStore extends
500: PropertyDataStore {
501: /**
502: * Creates a new ForceWGS84PropertyDataStore object.
503: *
504: * @param dir
505: * DOCUMENT ME!
506: */
507: public ForceWGS84PropertyDataStore(File dir) {
508: super (dir);
509: }
510:
511: /**
512: * DOCUMENT ME!
513: *
514: * @param typeName
515: * DOCUMENT ME!
516: *
517: * @return DOCUMENT ME!
518: *
519: * @throws IOException
520: * DOCUMENT ME!
521: * @throws DataSourceException
522: * DOCUMENT ME!
523: */
524: public FeatureType getSchema(String typeName)
525: throws IOException {
526: FeatureType schema = super .getSchema(typeName);
527:
528: try {
529: return DataUtilities.createSubType(schema, null,
530: DefaultGeographicCRS.WGS84);
531: } catch (SchemaException e) {
532: throw new DataSourceException(e.getMessage(), e);
533: }
534: }
535:
536: /**
537: * DOCUMENT ME!
538: */
539:
540: /*
541: * public FeatureReader getFeatureReader(Query query, Transaction
542: * transaction) throws IOException { FeatureReader reader =
543: * super.getFeatureReader(query, transaction); try { return new
544: * ForceCoordinateSystemFeatureReader(reader,
545: * AbstractCiteDataTest.FORCED_WGS84); } catch (SchemaException e) {
546: * throw new DataSourceException(e.getMessage(), e); } }
547: */
548: }
549: }
|