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.geoserver.wms.kvp;
0006:
0007: import java.io.ByteArrayInputStream;
0008: import java.io.IOException;
0009: import java.io.InputStream;
0010: import java.net.MalformedURLException;
0011: import java.net.URL;
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.Collections;
0015: import java.util.HashMap;
0016: import java.util.HashSet;
0017: import java.util.Iterator;
0018: import java.util.List;
0019: import java.util.Map;
0020: import java.util.Set;
0021: import java.util.logging.Level;
0022:
0023: import javax.servlet.http.HttpServletRequest;
0024:
0025: import org.geoserver.ows.HttpServletRequestAware;
0026: import org.geoserver.ows.KvpRequestReader;
0027: import org.geoserver.ows.util.KvpUtils;
0028: import org.geotools.data.DataStore;
0029: import org.geotools.data.DefaultQuery;
0030: import org.geotools.data.FeatureReader;
0031: import org.geotools.data.FeatureSource;
0032: import org.geotools.data.Query;
0033: import org.geotools.data.Transaction;
0034: import org.geotools.data.crs.ForceCoordinateSystemFeatureReader;
0035: import org.geotools.data.memory.MemoryDataStore;
0036: import org.geotools.data.wfs.WFSDataStore;
0037: import org.geotools.data.wfs.WFSDataStoreFactory;
0038: import org.geotools.factory.CommonFactoryFinder;
0039: import org.geotools.feature.FeatureType;
0040: import org.geotools.referencing.CRS;
0041: import org.geotools.referencing.crs.DefaultGeographicCRS;
0042: import org.geotools.styling.FeatureTypeConstraint;
0043: import org.geotools.styling.NamedLayer;
0044: import org.geotools.styling.NamedStyle;
0045: import org.geotools.styling.RemoteOWS;
0046: import org.geotools.styling.SLDParser;
0047: import org.geotools.styling.Style;
0048: import org.geotools.styling.StyleAttributeExtractor;
0049: import org.geotools.styling.StyleFactory;
0050: import org.geotools.styling.StyledLayer;
0051: import org.geotools.styling.StyledLayerDescriptor;
0052: import org.geotools.styling.UserLayer;
0053: import org.opengis.filter.Filter;
0054: import org.opengis.filter.FilterFactory;
0055: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0056: import org.vfny.geoserver.global.CoverageInfo;
0057: import org.vfny.geoserver.global.Data;
0058: import org.vfny.geoserver.global.FeatureTypeInfo;
0059: import org.vfny.geoserver.global.MapLayerInfo;
0060: import org.vfny.geoserver.global.TemporaryFeatureTypeInfo;
0061: import org.vfny.geoserver.global.WMS;
0062: import org.vfny.geoserver.util.Requests;
0063: import org.vfny.geoserver.util.SLDValidator;
0064: import org.vfny.geoserver.wms.WmsException;
0065: import org.vfny.geoserver.wms.requests.GetMapKvpReader;
0066: import org.vfny.geoserver.wms.requests.GetMapRequest;
0067: import org.vfny.geoserver.wms.servlets.GetMap;
0068:
0069: public class GetMapKvpRequestReader extends KvpRequestReader implements
0070: HttpServletRequestAware {
0071: /**
0072: * get map
0073: */
0074: GetMap getMap;
0075:
0076: /**
0077: * current request
0078: */
0079: HttpServletRequest httpRequest;
0080:
0081: /**
0082: * style factory
0083: */
0084: StyleFactory styleFactory = CommonFactoryFinder
0085: .getStyleFactory(null);
0086:
0087: /**
0088: * filter factory
0089: */
0090: FilterFactory filterFactory = CommonFactoryFinder
0091: .getFilterFactory(null);
0092:
0093: /**
0094: * Flag to control wether styles are mandatory
0095: */
0096: boolean styleRequired;
0097:
0098: /**
0099: * The WMS service, that we use to pick up base layer definitions
0100: */
0101: WMS wms;
0102:
0103: /**
0104: * The data catalog
0105: */
0106: Data catalog;
0107:
0108: /**
0109: * This flags allows the kvp reader to go beyond the SLD library mode specification
0110: * and match the first style that can be applied to a given layer. This is for
0111: * backwards compatibility
0112: */
0113: boolean laxStyleMatchAllowed = true;
0114:
0115: public GetMapKvpRequestReader(GetMap getMap, WMS wms) {
0116: super (GetMapRequest.class);
0117:
0118: this .getMap = getMap;
0119: this .wms = wms;
0120: this .catalog = wms.getData();
0121: }
0122:
0123: public void setHttpRequest(HttpServletRequest httpRequest) {
0124: this .httpRequest = httpRequest;
0125: }
0126:
0127: public void setStyleFactory(StyleFactory styleFactory) {
0128: this .styleFactory = styleFactory;
0129: }
0130:
0131: public void setFilterFactory(FilterFactory filterFactory) {
0132: this .filterFactory = filterFactory;
0133: }
0134:
0135: public boolean isStyleRequired() {
0136: return styleRequired;
0137: }
0138:
0139: public void setStyleRequired(boolean styleRequired) {
0140: this .styleRequired = styleRequired;
0141: }
0142:
0143: public Object createRequest() throws Exception {
0144: GetMapRequest request = new GetMapRequest(getMap);
0145: request.setHttpServletRequest(httpRequest);
0146:
0147: return request;
0148: }
0149:
0150: public Object read(Object request, Map kvp, Map rawKvp)
0151: throws Exception {
0152: GetMapRequest getMap = (GetMapRequest) super .read(request, kvp,
0153: rawKvp);
0154:
0155: // do some additional checks
0156:
0157: // srs
0158: String epsgCode = getMap.getSRS();
0159:
0160: if (epsgCode != null) {
0161: try {
0162: // set the crs as well
0163: CoordinateReferenceSystem mapcrs = CRS.decode(epsgCode);
0164: getMap.setCrs(mapcrs);
0165: } catch (Exception e) {
0166: // couldnt make it - we send off a service exception with the
0167: // correct info
0168: throw new WmsException(e.getLocalizedMessage(),
0169: "InvalidSRS");
0170: }
0171: }
0172:
0173: // remote OWS
0174: String remoteOwsType = getMap.getRemoteOwsType();
0175: remoteOwsType = remoteOwsType != null ? remoteOwsType
0176: .toUpperCase() : null;
0177: if (remoteOwsType != null && !"WFS".equals(remoteOwsType)) {
0178: throw new WmsException("Unsupported remote OWS type '"
0179: + remoteOwsType + "'");
0180: }
0181:
0182: // remote OWS url
0183: URL remoteOwsUrl = getMap.getRemoteOwsURL();
0184: if (remoteOwsUrl != null && remoteOwsType == null)
0185: throw new WmsException(
0186: "REMOTE_OWS_URL specified, but REMOTE_OWS_TYPE is missing");
0187:
0188: // layers
0189: String layerParam = (String) kvp.get("LAYERS");
0190: if (layerParam != null)
0191: getMap.setLayers(parseLayers(KvpUtils.readFlat(layerParam),
0192: remoteOwsUrl, remoteOwsType));
0193:
0194: // raw styles parameter
0195: String stylesParam = (String) kvp.get("STYLES");
0196: List styleNameList = Collections.EMPTY_LIST;
0197: if (stylesParam != null)
0198: styleNameList = KvpUtils.readFlat(stylesParam);
0199:
0200: // styles
0201: // process SLD_BODY, SLD, then STYLES parameter
0202: if (getMap.getSldBody() != null) {
0203: if (LOGGER.isLoggable(Level.FINE)) {
0204: LOGGER.fine("Getting layers and styles from SLD_BODY");
0205: }
0206:
0207: if (getMap.getValidateSchema().booleanValue()) {
0208: List errors = validateSld(new ByteArrayInputStream(
0209: getMap.getSldBody().getBytes()));
0210:
0211: if (errors.size() != 0) {
0212: throw new WmsException(SLDValidator
0213: .getErrorMessage(new ByteArrayInputStream(
0214: getMap.getSldBody().getBytes()),
0215: errors));
0216: }
0217: }
0218:
0219: StyledLayerDescriptor sld = parseSld(new ByteArrayInputStream(
0220: getMap.getSldBody().getBytes()));
0221: processSld(getMap, sld, styleNameList);
0222: } else if (getMap.getSld() != null) {
0223: if (LOGGER.isLoggable(Level.FINE)) {
0224: LOGGER
0225: .fine("Getting layers and styles from reomte SLD");
0226: }
0227:
0228: URL sldUrl = getMap.getSld();
0229:
0230: if (getMap.getValidateSchema().booleanValue()) {
0231: InputStream input = Requests.getInputStream(sldUrl);
0232: List errors = null;
0233:
0234: try {
0235: errors = validateSld(input);
0236: } finally {
0237: input.close();
0238: }
0239:
0240: if ((errors != null) && (errors.size() != 0)) {
0241: input = Requests.getInputStream(sldUrl);
0242:
0243: try {
0244: throw new WmsException(SLDValidator
0245: .getErrorMessage(input, errors));
0246: } finally {
0247: input.close();
0248: }
0249: }
0250: }
0251:
0252: // JD: GEOS-420, Wrap the sldUrl in getINputStream method in order
0253: // to do compression
0254: InputStream input = Requests.getInputStream(sldUrl);
0255:
0256: try {
0257: StyledLayerDescriptor sld = parseSld(input);
0258: processSld(getMap, sld, styleNameList);
0259: } finally {
0260: input.close();
0261: }
0262: } else {
0263: if (LOGGER.isLoggable(Level.FINE)) {
0264: LOGGER
0265: .fine("Getting layers and styles from LAYERS and STYLES");
0266: }
0267:
0268: // ok, parse the styles parameter in isolation
0269: if (styleNameList.size() > 0)
0270: getMap.setStyles(parseStyles(styleNameList));
0271:
0272: // first, expand base layers and default styles
0273: if (getMap.getLayers() != null) {
0274: List oldLayers = new ArrayList(Arrays.asList(getMap
0275: .getLayers()));
0276: List oldStyles = getMap.getStyles() != null ? new ArrayList(
0277: getMap.getStyles())
0278: : new ArrayList();
0279: List newLayers = new ArrayList();
0280: List newStyles = new ArrayList();
0281:
0282: for (int i = 0; i < oldLayers.size(); i++) {
0283: MapLayerInfo info = (MapLayerInfo) oldLayers.get(i);
0284: Style style = oldStyles.isEmpty() ? null
0285: : (Style) oldStyles.get(i);
0286: if (info.getType() == MapLayerInfo.TYPE_BASEMAP) {
0287: List subLayers = info.getSubLayers();
0288: newLayers.addAll(subLayers);
0289: List currStyles = info.getStyles();
0290: for (int j = 0; j < subLayers.size(); j++) {
0291: MapLayerInfo currLayer = (MapLayerInfo) subLayers
0292: .get(j);
0293: Style currStyle = currStyles.isEmpty() ? null
0294: : (Style) currStyles.get(j);
0295: if (currStyle != null)
0296: newStyles.add(currStyle);
0297: else
0298: newStyles.add(currLayer
0299: .getDefaultStyle());
0300: }
0301: } else {
0302: newLayers.add(info);
0303: if (style != null)
0304: newStyles.add(style);
0305: else
0306: newStyles.add(info.getDefaultStyle());
0307: }
0308: }
0309: getMap.setLayers(newLayers);
0310: getMap.setStyles(newStyles);
0311: }
0312:
0313: // then proceed with standard processing
0314: MapLayerInfo[] layers = getMap.getLayers();
0315: if ((layers != null) && (layers.length > 0)) {
0316: List styles = getMap.getStyles();
0317:
0318: if (layers.length != styles.size()) {
0319: String msg = layers.length
0320: + " layers requested, but found "
0321: + styles.size() + " styles specified. ";
0322: throw new WmsException(msg, getClass().getName());
0323: }
0324:
0325: for (int i = 0; i < getMap.getStyles().size(); i++) {
0326: Style currStyle = (Style) getMap.getStyles().get(i);
0327: if (currStyle == null)
0328: throw new WmsException(
0329: "Could not find a style for layer "
0330: + getMap.getLayers()[i]
0331: .getName()
0332: + ", either none was specified or no default style is available for it",
0333: "NoDefaultStyle");
0334: checkStyle(currStyle, layers[i]);
0335: if (LOGGER.isLoggable(Level.FINE)) {
0336: LOGGER
0337: .fine(new StringBuffer("establishing ")
0338: .append(currStyle.getName())
0339: .append(" style for ").append(
0340: layers[i].getName())
0341: .toString());
0342: }
0343: }
0344: }
0345: }
0346:
0347: // filters
0348: // in case of a mixed request, get with sld in post body, layers
0349: // are not parsed, so we can't parse filters neither...
0350: if ((getMap.getLayers() != null)
0351: && (getMap.getLayers().length > 0)) {
0352: List filters = (getMap.getFilter() != null) ? getMap
0353: .getFilter() : Collections.EMPTY_LIST;
0354: List cqlFilters = (getMap.getCQLFilter() != null) ? getMap
0355: .getCQLFilter() : Collections.EMPTY_LIST;
0356: List featureId = (getMap.getFeatureId() != null) ? getMap
0357: .getFeatureId() : Collections.EMPTY_LIST;
0358:
0359: if (!featureId.isEmpty()) {
0360: if (!filters.isEmpty()) {
0361: throw new WmsException(
0362: "GetMap KVP request contained "
0363: + "conflicting filters. Filter: "
0364: + filters + ", fid: " + featureId);
0365: }
0366:
0367: Set ids = new HashSet();
0368: for (Iterator i = featureId.iterator(); i.hasNext();) {
0369: ids.add(filterFactory.featureId((String) i.next()));
0370: }
0371: filters = Collections.singletonList(filterFactory
0372: .id(ids));
0373: }
0374:
0375: if (!cqlFilters.isEmpty()) {
0376: if (!filters.isEmpty()) {
0377: throw new WmsException(
0378: "GetMap KVP request contained "
0379: + "conflicting filters. Filter: "
0380: + filters + ", fid: " + featureId
0381: + ", cql: " + cqlFilters);
0382: }
0383:
0384: filters = cqlFilters;
0385: }
0386:
0387: int numLayers = getMap.getLayers().length;
0388:
0389: if (!filters.isEmpty() && (numLayers != filters.size())) {
0390: // as in wfs getFeatures, perform lenient parsing, if just one
0391: // filter, it gets
0392: // applied to all layers
0393: if (filters.size() == 1) {
0394: Filter f = (Filter) filters.get(0);
0395: filters = new ArrayList(numLayers);
0396:
0397: for (int i = 0; i < numLayers; i++) {
0398: filters.add(f);
0399: }
0400: } else {
0401: String msg = numLayers
0402: + " layers requested, but found "
0403: + filters.size()
0404: + " filters specified. "
0405: + "When you specify the FILTER parameter, you must provide just one, \n"
0406: + " that will be applied to all layers, or exactly one for each requested layer";
0407: throw new WmsException(msg, getClass().getName());
0408: }
0409: }
0410:
0411: getMap.setFilter(filters);
0412: }
0413:
0414: // set the raw params used to create the request
0415: getMap.setRawKvp(rawKvp);
0416:
0417: return getMap;
0418: }
0419:
0420: /**
0421: * validates an sld document.
0422: *
0423: */
0424: private List validateSld(InputStream input) {
0425: // user requested to validate the schema.
0426: SLDValidator validator = new SLDValidator();
0427:
0428: return validator.validateSLD(input, httpRequest.getSession()
0429: .getServletContext());
0430: }
0431:
0432: /**
0433: * Parses an sld document.
0434: */
0435: private StyledLayerDescriptor parseSld(InputStream input) {
0436: SLDParser parser = new SLDParser(styleFactory, input);
0437:
0438: return parser.parseSLD();
0439: }
0440:
0441: public void processSld(final GetMapRequest request,
0442: final StyledLayerDescriptor sld, final List styleNames)
0443: throws WmsException {
0444: if (request.getLayers() == null
0445: || request.getLayers().length == 0) {
0446: processStandaloneSld(request, sld);
0447: } else {
0448: processLibrarySld(request, sld, styleNames);
0449: }
0450: }
0451:
0452: /**
0453: * Looks in <code>sld</code> for the layers and styles to use in the map
0454: * composition and sets them to the <code>request</code>
0455: *
0456: * <p>
0457: * This method processes SLD in library mode
0458: * Library mode engages when "SLD" or "SLD_BODY" are used in conjuction with
0459: * LAYERS and STYLES. From the spec: <br>
0460: * <cite> When an SLD is used as a style
0461: * library, the STYLES CGI parameter is interpreted in the usual way in the
0462: * GetMap request, except that the handling of the style names is organized
0463: * so that the styles defined in the SLD take precedence over the named
0464: * styles stored within the map server. The user-defined SLD styles can be
0465: * given names and they can be marked as being the default style for a
0466: * layer. To be more specific, if a style named “CenterLine” is referenced
0467: * for a layer and a style with that name is defined for the corresponding
0468: * layer in the SLD, then the SLD style definition is used. Otherwise, the
0469: * standard named-style mechanism built into the map server is used. If the
0470: * use of a default style is specified and a style is marked as being the
0471: * default for the corresponding layer in the SLD, then the default style
0472: * from the SLD is used; otherwise, the standard default style in the map
0473: * server is used. </cite>
0474: *
0475: * @param request
0476: * the GetMap request to which to set the layers and styles
0477: * @param sld
0478: * a SLD document to take layers and styles from, following the
0479: * "literal" or "library" rule.
0480: *
0481: * @throws WmsException
0482: * if anything goes wrong
0483: * @throws RuntimeException
0484: * DOCUMENT ME!
0485: */
0486: private void processLibrarySld(final GetMapRequest request,
0487: final StyledLayerDescriptor sld, final List styleNames) {
0488: final StyledLayer[] styledLayers = sld.getStyledLayers();
0489: final int slCount = styledLayers.length;
0490:
0491: if (slCount == 0) {
0492: throw new WmsException("SLD document contains no layers");
0493: }
0494:
0495: final List layers = new ArrayList();
0496: final List styles = new ArrayList();
0497: MapLayerInfo[] libraryModeLayers = request.getLayers();
0498: MapLayerInfo currLayer = null;
0499: Style currStyle = null;
0500: String styleName = null;
0501:
0502: for (int i = 0; i < libraryModeLayers.length; i++) {
0503: currLayer = libraryModeLayers[i];
0504: if (styleNames != null && styleNames.size() > 0)
0505: styleName = (String) styleNames.get(i);
0506:
0507: // base map layers do not participate in library mode
0508: if (currLayer.getType() == MapLayerInfo.TYPE_BASEMAP) {
0509: List subLayers = currLayer.getSubLayers();
0510: layers.addAll(subLayers);
0511: List currStyles = currLayer.getStyles();
0512: for (int j = 0; j < subLayers.size(); j++) {
0513: MapLayerInfo l = (MapLayerInfo) subLayers.get(j);
0514: Style s = currStyles.isEmpty() ? null
0515: : (Style) currStyles.get(j);
0516: if (s != null)
0517: styles.add(currStyle);
0518: else
0519: styles.add(currLayer.getDefaultStyle());
0520: }
0521: } else {
0522: layers.add(currLayer);
0523: styles.add(findStyleOf(request, currLayer, styleName,
0524: styledLayers));
0525: }
0526: }
0527:
0528: request.setLayers((MapLayerInfo[]) layers
0529: .toArray(new MapLayerInfo[layers.size()]));
0530: request.setStyles(styles);
0531: }
0532:
0533: /**
0534: * This one processes an SLD in non library mode, that is, it assumes it's the
0535: * definition of the map
0536: * @param request
0537: * @param sld
0538: */
0539: public static void processStandaloneSld(
0540: final GetMapRequest request, final StyledLayerDescriptor sld) {
0541: final StyledLayer[] styledLayers = sld.getStyledLayers();
0542: final int slCount = styledLayers.length;
0543:
0544: if (slCount == 0) {
0545: throw new WmsException("SLD document contains no layers");
0546: }
0547:
0548: final List layers = new ArrayList();
0549: final List styles = new ArrayList();
0550: MapLayerInfo currLayer = null;
0551: Style currStyle = null;
0552:
0553: StyledLayer sl = null;
0554: String layerName;
0555: UserLayer ul;
0556:
0557: for (int i = 0; i < slCount; i++) {
0558: sl = styledLayers[i];
0559: layerName = sl.getName();
0560:
0561: if (null == layerName) {
0562: throw new WmsException(
0563: "A UserLayer without layer name was passed");
0564: }
0565:
0566: if (sl instanceof UserLayer
0567: && ((((UserLayer) sl)).getRemoteOWS() != null)) {
0568: // this beast can define multiple feature sources and multiple styles, we'll
0569: // have to mix and match them (ugh)
0570: ul = ((UserLayer) sl);
0571: try {
0572: addRemoteLayersFromUserLayer(request, ul, layers,
0573: styles);
0574: } catch (IOException e) {
0575: throw new WmsException(
0576: "Error accessing remote layers",
0577: "RemoteAccessFailed", e);
0578: }
0579: } else {
0580: // simpler case, one layer, eventually multiple styles
0581: currLayer = new MapLayerInfo();
0582:
0583: // handle the InLineFeature stuff
0584: // TODO: add support for remote WFS here
0585: if ((sl instanceof UserLayer)
0586: && ((((UserLayer) sl))
0587: .getInlineFeatureDatastore() != null)) {
0588: // SPECIAL CASE - we make the temporary version
0589: ul = ((UserLayer) sl);
0590:
0591: try {
0592: initializeInlineFeatureLayer(request, ul,
0593: currLayer);
0594: } catch (Exception e) {
0595: throw new WmsException(e);
0596: }
0597: } else {
0598: try {
0599: currLayer.setFeature(GetMapKvpReader
0600: .findFeatureLayer(request, layerName));
0601: } catch (WmsException e) {
0602: currLayer.setCoverage(GetMapKvpReader
0603: .findCoverageLayer(request, layerName));
0604: }
0605: }
0606:
0607: if (currLayer.getType() == MapLayerInfo.TYPE_VECTOR) {
0608: // currStyle = findStyleOf(request, currLayer,
0609: // styledLayers); // DJB: this looks like a bug, we should
0610: // get the style from styledLayers[i]
0611:
0612: // the correct thing to do its grab the style from
0613: // styledLayers[i]
0614: // inside the styledLayers[i] will either be :
0615: // a) nothing - in which case grab the layer's default style
0616: // b) a set of:
0617: // i) NameStyle -- grab it from the pre-loaded styles
0618: // ii)UserStyle -- grab it from the sld the user uploaded
0619: //
0620: // NOTE: we're going to get a set of layer->style pairs for
0621: // (b).
0622: addStyles(request, currLayer, styledLayers[i],
0623: layers, styles);
0624: } else if (currLayer.getType() == MapLayerInfo.TYPE_RASTER) {
0625: try {
0626: addStyles(request, currLayer, styledLayers[i],
0627: layers, styles);
0628: } catch (WmsException wm) {
0629: // hmm, well, the style they specified in the wms
0630: // request
0631: // wasn't found. Let's try the default raster style
0632: // named 'raster'
0633: currStyle = findStyle(request, "raster");
0634: if (currStyle == null) {
0635: // nope, no default raster style either. Give up.
0636: throw new WmsException(
0637: wm.getMessage()
0638: + " Also tried to use "
0639: + "the generic raster style 'raster', but it wasn't available.");
0640: }
0641: layers.add(currLayer);
0642: styles.add(currStyle);
0643: }
0644: }
0645: }
0646: }
0647:
0648: request.setLayers((MapLayerInfo[]) layers
0649: .toArray(new MapLayerInfo[layers.size()]));
0650: request.setStyles(styles);
0651: }
0652:
0653: private static void addRemoteLayersFromUserLayer(
0654: GetMapRequest request, UserLayer ul, List layers,
0655: List styles) throws WmsException, IOException {
0656: RemoteOWS service = ul.getRemoteOWS();
0657: if (!service.getService().equalsIgnoreCase("WFS"))
0658: throw new WmsException(
0659: "GeoServer only supports WFS as remoteOWS service");
0660: if (service.getOnlineResource() == null)
0661: throw new WmsException(
0662: "OnlineResource for remote WFS not specified in SLD");
0663: final FeatureTypeConstraint[] featureConstraints = ul
0664: .getLayerFeatureConstraints();
0665: if (featureConstraints == null
0666: || featureConstraints.length == 0)
0667: throw new WmsException(
0668: "No FeatureTypeConstraint specified, no layer can be loaded for this UserStyle");
0669:
0670: DataStore remoteWFS = null;
0671: List remoteTypeNames = null;
0672: try {
0673: URL url = new URL(service.getOnlineResource());
0674: remoteWFS = connectRemoteWFS(url);
0675: remoteTypeNames = new ArrayList(Arrays.asList(remoteWFS
0676: .getTypeNames()));
0677: Collections.sort(remoteTypeNames);
0678: } catch (MalformedURLException e) {
0679: throw new WmsException("Invalid online resource url: '"
0680: + service.getOnlineResource() + "'");
0681: }
0682:
0683: Style[] layerStyles = ul.getUserStyles();
0684: if (request.getFilter() == null)
0685: request.setFilter(new ArrayList());
0686: for (int i = 0; i < featureConstraints.length; i++) {
0687: // make sure the layer is there
0688: String name = featureConstraints[i].getFeatureTypeName();
0689: if (Collections.binarySearch(remoteTypeNames, name) < 0) {
0690: throw new WmsException(
0691: "Could not find layer feature type '" + name
0692: + "' on remote WFS '"
0693: + service.getOnlineResource());
0694: }
0695:
0696: // grab the filter
0697: Filter filter = featureConstraints[i].getFilter();
0698: if (filter == null)
0699: filter = Filter.INCLUDE;
0700:
0701: // connect the layer
0702: FeatureSource fs = remoteWFS.getFeatureSource(name);
0703:
0704: // this is messy, why the spec allows for multiple constraints and multiple
0705: // styles is beyond me... we'll style each remote layer with all possible
0706: // styles, feauture type style matching will do the rest during rendering
0707: for (int j = 0; j < layerStyles.length; j++) {
0708: Style style = layerStyles[i];
0709: MapLayerInfo info = new MapLayerInfo(fs);
0710: layers.add(info);
0711: styles.add(style);
0712: // treat it like an externally provided filter... ugly I know, but
0713: // the sane thing (adding a filter as a MapLayerInfo field) would
0714: // break havoc in GetFeatureInfo
0715: request.getFilter().add(filter);
0716: }
0717: }
0718: }
0719:
0720: /**
0721: * the correct thing to do its grab the style from styledLayers[i] inside
0722: * the styledLayers[i] will either be : a) nothing - in which case grab the
0723: * layer's default style b) a set of: i) NameStyle -- grab it from the
0724: * pre-loaded styles ii)UserStyle -- grab it from the sld the user uploaded
0725: *
0726: * NOTE: we're going to get a set of layer->style pairs for (b). these are
0727: * added to layers,styles
0728: *
0729: * NOTE: we also handle some featuretypeconstraints
0730: *
0731: * @param request
0732: * @param currLayer
0733: * @param layer
0734: * @param layers
0735: * @param styles
0736: */
0737: public static void addStyles(GetMapRequest request,
0738: MapLayerInfo currLayer, StyledLayer layer, List layers,
0739: List styles) throws WmsException {
0740: if (currLayer == null) {
0741: return; // protection
0742: }
0743:
0744: Style[] layerStyles = null;
0745: FeatureTypeConstraint[] ftcs = null;
0746:
0747: if (layer instanceof NamedLayer) {
0748: ftcs = ((NamedLayer) layer).getLayerFeatureConstraints();
0749: layerStyles = ((NamedLayer) layer).getStyles();
0750: } else if (layer instanceof UserLayer) {
0751: ftcs = ((UserLayer) layer).getLayerFeatureConstraints();
0752: layerStyles = ((UserLayer) layer).getUserStyles();
0753: }
0754:
0755: // DJB: TODO: this needs to do the whole thing, not just names
0756: if (ftcs != null) {
0757: FeatureTypeConstraint ftc;
0758: final int length = ftcs.length;
0759:
0760: for (int t = 0; t < length; t++) {
0761: ftc = ftcs[t];
0762:
0763: if (ftc.getFeatureTypeName() != null) {
0764: String ftc_name = ftc.getFeatureTypeName();
0765:
0766: // taken from lite renderer
0767: boolean matches;
0768:
0769: try {
0770: matches = currLayer.getFeature()
0771: .getFeatureType().isDescendedFrom(null,
0772: ftc_name)
0773: || currLayer.getFeature()
0774: .getFeatureType().getTypeName()
0775: .equalsIgnoreCase(ftc_name);
0776: } catch (Exception e) {
0777: matches = false; // bad news
0778: }
0779:
0780: if (!matches) {
0781: continue; // this layer is fitered out
0782: }
0783: }
0784: }
0785: }
0786:
0787: // handle no styles -- use default
0788: if ((layerStyles == null) || (layerStyles.length == 0)) {
0789: layers.add(currLayer);
0790: styles.add(currLayer.getDefaultStyle());
0791:
0792: return;
0793: }
0794:
0795: final int length = layerStyles.length;
0796: Style s;
0797:
0798: for (int t = 0; t < length; t++) {
0799: if (layerStyles[t] instanceof NamedStyle) {
0800: layers.add(currLayer);
0801: s = findStyle(request, ((NamedStyle) layerStyles[t])
0802: .getName());
0803:
0804: if (s == null) {
0805: throw new WmsException("couldnt find style named '"
0806: + ((NamedStyle) layerStyles[t]).getName()
0807: + "'");
0808: }
0809:
0810: styles.add(s);
0811: } else {
0812: layers.add(currLayer);
0813: styles.add(layerStyles[t]);
0814: }
0815: }
0816: }
0817:
0818: /**
0819: * DOCUMENT ME!
0820: *
0821: * @param request
0822: * @param currStyleName
0823: *
0824: * @return the configured style named <code>currStyleName</code> or
0825: * <code>null</code> if such a style does not exists on this
0826: * server.
0827: */
0828: public static Style findStyle(GetMapRequest request,
0829: String currStyleName) {
0830: Style currStyle;
0831: Map configuredStyles = request.getWMS().getData().getStyles();
0832:
0833: currStyle = (Style) configuredStyles.get(currStyleName);
0834:
0835: return currStyle;
0836: }
0837:
0838: /**
0839: * Finds the style for <code>layer</code> in <code>styledLayers</code>
0840: * or the layer's default style if <code>styledLayers</code> has no a
0841: * UserLayer or a NamedLayer with the same name than <code>layer</code>
0842: * <p>
0843: * This method is used to parse the style of a layer for SLD and SLD_BODY
0844: * parameters, both in library and literal mode. Thus, once the declared
0845: * style for the given layer is found, it is checked for validity of
0846: * appliance for that layer (i.e., whether the featuretype contains the
0847: * attributes needed for executing the style filters).
0848: * </p>
0849: *
0850: * @param request
0851: * used to find out an internally configured style when
0852: * referenced by name by a NamedLayer
0853: *
0854: * @param layer
0855: * one of the layers that was requested through the
0856: * LAYERS parameter or through and SLD document when the request
0857: * is in literal mode.
0858: * @param styledLayers
0859: * a set of StyledLayers from where to find the SLD layer with
0860: * the same name as <code>layer</code> and extract the style to
0861: * apply.
0862: *
0863: * @return the Style applicable to <code>layer</code> extracted from
0864: * <code>styledLayers</code>.
0865: *
0866: * @throws RuntimeException
0867: * if one of the StyledLayers is neither a UserLayer nor a
0868: * NamedLayer. This shuoldn't happen, since the only allowed
0869: * subinterfaces of StyledLayer are NamedLayer and UserLayer.
0870: * @throws WmsException
0871: */
0872: private Style findStyleOf(GetMapRequest request,
0873: MapLayerInfo layer, String styleName,
0874: StyledLayer[] styledLayers) throws WmsException {
0875: Style style = null;
0876: String layerName = layer.getName();
0877: StyledLayer sl;
0878:
0879: for (int i = 0; i < styledLayers.length; i++) {
0880: sl = styledLayers[i];
0881:
0882: if (layerName.equals(sl.getName())) {
0883: if (sl instanceof UserLayer) {
0884: Style[] styles = ((UserLayer) sl).getUserStyles();
0885:
0886: // if the style name has not been specified, look it up
0887: // the default style, otherwise lookup the one requested
0888: for (int j = 0; style == null && styles != null
0889: && j < styles.length; j++) {
0890: if (styleName == null || styleName.equals("")
0891: && styles[j].isDefault())
0892: style = styles[j];
0893: else if (styleName != null
0894: && styleName
0895: .equals(styles[j].getName()))
0896: style = styles[j];
0897: }
0898: } else if (sl instanceof NamedLayer) {
0899: Style[] styles = ((NamedLayer) sl).getStyles();
0900:
0901: // if the style name has not been specified, look it up
0902: // the default style, otherwise lookup the one requested
0903: for (int j = 0; style == null && styles != null
0904: && j < styles.length; j++) {
0905: if ((styleName == null || styleName.equals(""))
0906: && styles[j].isDefault())
0907: style = styles[j];
0908: else if (styleName != null
0909: && styleName
0910: .equals(styles[j].getName()))
0911: style = styles[j];
0912: }
0913:
0914: if (style instanceof NamedStyle) {
0915: style = findStyle(request, style.getName());
0916: }
0917: } else {
0918: throw new RuntimeException("Unknown layer type: "
0919: + sl);
0920: }
0921:
0922: break;
0923: }
0924: }
0925:
0926: // fallback on the old GeoServer behaviour, if the style is not found find
0927: // the first style that matches the type name
0928: // TODO: would be nice to have a switch to turn this off since it's out of the spec
0929: if (style == null && laxStyleMatchAllowed) {
0930: for (int i = 0; i < styledLayers.length; i++) {
0931: sl = styledLayers[i];
0932:
0933: if (layerName.equals(sl.getName())) {
0934: if (sl instanceof UserLayer) {
0935: Style[] styles = ((UserLayer) sl)
0936: .getUserStyles();
0937:
0938: if ((null != styles) && (0 < styles.length)) {
0939: style = styles[0];
0940: }
0941: } else if (sl instanceof NamedLayer) {
0942: Style[] styles = ((NamedLayer) sl).getStyles();
0943:
0944: if ((null != styles) && (0 < styles.length)) {
0945: style = styles[0];
0946: }
0947:
0948: if (style instanceof NamedStyle) {
0949: style = findStyle(request, style.getName());
0950: }
0951: } else {
0952: throw new RuntimeException(
0953: "Unknown layer type: " + sl);
0954: }
0955:
0956: break;
0957: }
0958: }
0959: }
0960:
0961: // still not found? Fall back on the server default ones
0962: if (style == null) {
0963: if (styleName == null || "".equals(styleName)) {
0964: style = layer.getDefaultStyle();
0965: if (style == null)
0966: throw new WmsException(
0967: "Could not find a default style for "
0968: + layer.getName());
0969: } else {
0970: style = catalog.getStyle(styleName);
0971: if (style == null) {
0972: String msg = "No such style: " + styleName;
0973: throw new WmsException(msg, "StyleNotDefined");
0974: }
0975: }
0976: }
0977:
0978: checkStyle(style, layer);
0979:
0980: return style;
0981: }
0982:
0983: /**
0984: * Checks to make sure that the style passed in can process the FeatureType.
0985: *
0986: * @param style
0987: * The style to check
0988: * @param layer
0989: * The source requested.
0990: *
0991: * @throws WmsException
0992: * DOCUMENT ME!
0993: */
0994: private static void checkStyle(Style style, MapLayerInfo layer)
0995: throws WmsException {
0996: if (layer.getType() == layer.TYPE_BASEMAP
0997: || layer.getType() == layer.TYPE_RASTER)
0998: return;
0999:
1000: // extract attributes used in the style
1001: StyleAttributeExtractor sae = new StyleAttributeExtractor();
1002: sae.visit(style);
1003: String[] styleAttributes = sae.getAttributeNames();
1004:
1005: // see if we can collect any attribute out of the provided layer
1006: Set attributes = new HashSet();
1007: if (layer.getType() == MapLayerInfo.TYPE_VECTOR
1008: || layer.getType() == MapLayerInfo.TYPE_REMOTE_VECTOR) {
1009: try {
1010: final FeatureType type;
1011: if (layer.getType() == MapLayerInfo.TYPE_VECTOR)
1012: type = layer.getFeature().getFeatureType();
1013: else
1014: type = layer.getRemoteFeatureSource().getSchema();
1015: for (int i = 0; i < type.getAttributeCount(); i++) {
1016: attributes.add(type.getAttributeType(i)
1017: .getLocalName());
1018: }
1019: } catch (IOException ioe) {
1020: throw new RuntimeException(
1021: "Error getting FeatureType, this should never happen!");
1022: }
1023: }
1024:
1025: // check all attributes required by the style are available
1026: String attName;
1027: final int length = styleAttributes.length;
1028: for (int i = 0; i < length; i++) {
1029: attName = styleAttributes[i];
1030:
1031: if (!attributes.contains(attName)) {
1032: throw new WmsException(
1033: "The requested Style can not be used with this layer. The style specifies "
1034: + "an attribute of " + attName
1035: + " and the layer is: "
1036: + layer.getName());
1037: }
1038: }
1039: }
1040:
1041: /**
1042: * Method to initialize a user layer which contains inline features.
1043: *
1044: * @param httpRequest
1045: * The request
1046: * @param mapLayer
1047: * The map layer.
1048: *
1049: * @throws Exception
1050: */
1051:
1052: // JD: the reason this method is static is to share logic among the xml
1053: // and kvp reader, ugh...
1054: public static void initializeInlineFeatureLayer(
1055: GetMapRequest getMapRequest, UserLayer ul,
1056: MapLayerInfo currLayer) throws Exception {
1057: // SPECIAL CASE - we make the temporary version
1058: currLayer.setFeature(new TemporaryFeatureTypeInfo(ul
1059: .getInlineFeatureDatastore()));
1060:
1061: // what if they didn't put an "srsName" on their geometry in their
1062: // inlinefeature?
1063: // I guess we should assume they mean their geometry to exist in the
1064: // output SRS of the
1065: // request they're making.
1066: if (ul.getInlineFeatureType().getDefaultGeometry()
1067: .getCoordinateSystem() == null) {
1068: LOGGER
1069: .warning("No CRS set on inline features default geometry. Assuming the requestor has their inlinefeatures in the boundingbox CRS.");
1070:
1071: FeatureType currFt = ul.getInlineFeatureType();
1072: Query q = new DefaultQuery(currFt.getTypeName(),
1073: Filter.INCLUDE);
1074: FeatureReader ilReader = ul.getInlineFeatureDatastore()
1075: .getFeatureReader(q, Transaction.AUTO_COMMIT);
1076: CoordinateReferenceSystem crs = (getMapRequest.getCrs() == null) ? DefaultGeographicCRS.WGS84
1077: : getMapRequest.getCrs();
1078: MemoryDataStore reTypedDS = new MemoryDataStore(
1079: new ForceCoordinateSystemFeatureReader(ilReader,
1080: crs));
1081: currLayer
1082: .setFeature(new TemporaryFeatureTypeInfo(reTypedDS));
1083: }
1084: }
1085:
1086: protected MapLayerInfo[] parseLayers(List values, URL remoteOwsUrl,
1087: String remoteOwsType) throws Exception {
1088: List layers = new ArrayList();
1089: List layerNames = values;
1090: String layerName = null;
1091:
1092: // Grab remote OWS data store if needed
1093: DataStore remoteWFS = null;
1094: List remoteTypeNames = null;
1095: if ("WFS".equals(remoteOwsType) && remoteOwsUrl != null) {
1096: remoteWFS = connectRemoteWFS(remoteOwsUrl);
1097: remoteTypeNames = new ArrayList(Arrays.asList(remoteWFS
1098: .getTypeNames()));
1099: Collections.sort(remoteTypeNames);
1100: }
1101:
1102: ////
1103: // Layer lookup requires to:
1104: // * Look into the remote OWS first
1105: // * Look among the local layers
1106: // * expand local grouped layers (flatten them)
1107: ////
1108: for (Iterator it = layerNames.iterator(); it.hasNext();) {
1109: layerName = (String) it.next();
1110:
1111: // search into the remote WFS if there is any
1112: if (remoteTypeNames != null
1113: && Collections.binarySearch(remoteTypeNames,
1114: layerName) >= 0) {
1115: FeatureSource remoteSource = remoteWFS
1116: .getFeatureSource(layerName);
1117: if (remoteSource != null)
1118: layers.add(new MapLayerInfo(remoteSource));
1119: continue;
1120: }
1121:
1122: Integer layerType = catalog.getLayerType(layerName);
1123: if (layerType != null) {
1124: layers.add(buildMapLayerInfo(layerName));
1125: } else {
1126: if (wms.getBaseMapLayers().containsKey(layerName)) {
1127: layers.add(buildMapLayerInfo(layerName));
1128: } else {
1129: ////
1130: // Search for grouped layers (attention: heavy process)
1131: ////
1132: boolean found = false;
1133: String catalogLayerName = null;
1134:
1135: for (Iterator c_keys = catalog.getLayerNames()
1136: .iterator(); c_keys.hasNext();) {
1137: catalogLayerName = (String) c_keys.next();
1138:
1139: try {
1140: FeatureTypeInfo ftype = findFeatureLayer(catalogLayerName);
1141: String wmsPath = ftype.getWmsPath();
1142:
1143: if ((wmsPath != null)
1144: && wmsPath.matches(".*/"
1145: + layerName)) {
1146: layers
1147: .add(buildMapLayerInfo(catalogLayerName));
1148: found = true;
1149: }
1150: } catch (Exception e_1) {
1151: try {
1152: CoverageInfo cv = findCoverageLayer(catalogLayerName);
1153: String wmsPath = cv.getWmsPath();
1154:
1155: if ((wmsPath != null)
1156: && wmsPath.matches(".*/"
1157: + layerName)) {
1158: layers
1159: .add(buildMapLayerInfo(catalogLayerName));
1160: found = true;
1161: }
1162: } catch (Exception e_2) {
1163: }
1164: }
1165: }
1166: if (!found)
1167: throw new WmsException("Could not find layer "
1168: + layerName, "LayerNotDefined");
1169: }
1170:
1171: }
1172: }
1173:
1174: if (layers.size() == 0) {
1175: throw new WmsException("No LAYERS has been requested",
1176: getClass().getName());
1177: }
1178: return (MapLayerInfo[]) layers.toArray(new MapLayerInfo[layers
1179: .size()]);
1180: }
1181:
1182: private static DataStore connectRemoteWFS(URL remoteOwsUrl)
1183: throws WmsException {
1184: try {
1185: WFSDataStoreFactory factory = new WFSDataStoreFactory();
1186: Map params = new HashMap(factory.getImplementationHints());
1187: params.put(WFSDataStoreFactory.URL.key, remoteOwsUrl
1188: + "request=GetCapabilities&service=WFS");
1189: params.put(WFSDataStoreFactory.TRY_GZIP.key, Boolean.TRUE);
1190: return factory.createDataStore(params);
1191: } catch (Exception e) {
1192: throw new WmsException("Could not connect to remote OWS",
1193: "RemoteOWSFailure", e);
1194: }
1195: }
1196:
1197: private MapLayerInfo buildMapLayerInfo(String layerName)
1198: throws Exception {
1199: MapLayerInfo li = new MapLayerInfo();
1200:
1201: FeatureTypeInfo ftype = findFeatureLayer(layerName);
1202: if (ftype != null) {
1203: li.setFeature(ftype);
1204: } else {
1205: CoverageInfo cv = findCoverageLayer(layerName);
1206: if (cv != null) {
1207: li.setCoverage(cv);
1208: } else {
1209: if (wms.getBaseMapLayers().containsKey(layerName)) {
1210: String styleCsl = (String) wms.getBaseMapStyles()
1211: .get(layerName);
1212: String layerCsl = (String) wms.getBaseMapLayers()
1213: .get(layerName);
1214: MapLayerInfo[] layerArray = (MapLayerInfo[]) parseLayers(
1215: KvpUtils.readFlat(layerCsl), null, null);
1216: List styleList = (List) parseStyles(KvpUtils
1217: .readFlat(styleCsl));
1218: li.setBase(layerName, new ArrayList(Arrays
1219: .asList(layerArray)), styleList);
1220: } else {
1221: throw new WmsException("Layer " + layerName
1222: + " could not be found");
1223: }
1224: }
1225: }
1226: return li;
1227: }
1228:
1229: FeatureTypeInfo findFeatureLayer(String layerName)
1230: throws WmsException {
1231: FeatureTypeInfo ftype = null;
1232: Integer layerType = catalog.getLayerType(layerName);
1233:
1234: if (Data.TYPE_VECTOR != layerType) {
1235: return null;
1236: } else {
1237: ftype = catalog.getFeatureTypeInfo(layerName);
1238: }
1239:
1240: return ftype;
1241: }
1242:
1243: CoverageInfo findCoverageLayer(String layerName)
1244: throws WmsException {
1245: CoverageInfo cv = null;
1246: Integer layerType = catalog.getLayerType(layerName);
1247:
1248: if (Data.TYPE_RASTER != layerType) {
1249: return null;
1250: } else {
1251: cv = catalog.getCoverageInfo(layerName);
1252: }
1253:
1254: return cv;
1255: }
1256:
1257: protected List parseStyles(List styleNames) throws Exception {
1258: List styles = new ArrayList();
1259: for (Iterator it = styleNames.iterator(); it.hasNext();) {
1260: String styleid = (String) it.next();
1261:
1262: if ("".equals(styleid)) {
1263: //return null, this should flag request reader to use default for
1264: // the associated layer
1265: styles.add(null);
1266: } else {
1267: final Style style = catalog.getStyle(styleid);
1268: if (style == null) {
1269: String msg = "No such style: " + styleid;
1270: throw new WmsException(msg, "StyleNotDefined");
1271: }
1272: styles.add(style);
1273: }
1274: }
1275: return styles;
1276: }
1277:
1278: /**
1279: * This flags allows the kvp reader to go beyond the SLD library mode specification
1280: * and match the first style that can be applied to a given layer. This is for
1281: * backwards compatibility
1282: */
1283: public boolean isLaxStyleMatchAllowed() {
1284: return laxStyleMatchAllowed;
1285: }
1286:
1287: public void setLaxStyleMatchAllowed(boolean laxStyleMatchAllowed) {
1288: this.laxStyleMatchAllowed = laxStyleMatchAllowed;
1289: }
1290: }
|