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.sld.servlets;
006:
007: import org.geoserver.ows.util.XmlCharsetDetector;
008: import org.geotools.styling.SLDParser;
009: import org.geotools.styling.StyleFactory;
010: import org.geotools.styling.StyleFactoryFinder;
011: import org.geotools.styling.StyledLayerDescriptor;
012: import org.vfny.geoserver.ExceptionHandler;
013: import org.vfny.geoserver.Response;
014: import org.vfny.geoserver.ServiceException;
015: import org.vfny.geoserver.config.ConfigRequests;
016: import org.vfny.geoserver.config.DataConfig;
017: import org.vfny.geoserver.config.FeatureTypeConfig;
018: import org.vfny.geoserver.config.StyleConfig;
019: import org.vfny.geoserver.global.ConfigurationException;
020: import org.vfny.geoserver.global.GeoserverDataDirectory;
021: import org.vfny.geoserver.global.WMS;
022: import org.vfny.geoserver.servlets.AbstractService;
023: import org.vfny.geoserver.sld.SldException;
024: import org.vfny.geoserver.sld.requests.PutStylesKvpReader;
025: import org.vfny.geoserver.sld.requests.PutStylesRequest;
026: import org.vfny.geoserver.sld.responses.PutStylesResponse;
027: import org.vfny.geoserver.util.SLDValidator;
028: import org.vfny.geoserver.util.requests.readers.KvpRequestReader;
029: import org.vfny.geoserver.util.requests.readers.XmlRequestReader;
030: import org.vfny.geoserver.wms.WmsException;
031: import org.vfny.geoserver.wms.requests.GetMapXmlReader;
032: import org.w3c.dom.Node;
033: import org.w3c.dom.NodeList;
034: import org.xml.sax.InputSource;
035: import org.xml.sax.SAXException;
036: import java.io.BufferedInputStream;
037: import java.io.BufferedOutputStream;
038: import java.io.BufferedReader;
039: import java.io.CharArrayReader;
040: import java.io.File;
041: import java.io.FileInputStream;
042: import java.io.FileOutputStream;
043: import java.io.FileReader;
044: import java.io.FileWriter;
045: import java.io.IOException;
046: import java.io.Reader;
047: import java.math.BigDecimal;
048: import java.util.Enumeration;
049: import java.util.HashMap;
050: import java.util.Iterator;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.logging.Logger;
054: import javax.servlet.ServletContext;
055: import javax.servlet.ServletException;
056: import javax.servlet.http.HttpServletRequest;
057: import javax.servlet.http.HttpServletResponse;
058: import javax.xml.parsers.ParserConfigurationException;
059:
060: public class PutStyles extends AbstractService {
061: private static Logger LOGGER = org.geotools.util.logging.Logging
062: .getLogger("org.vfny.geoserver.sld.servlets");
063: public final String success_mime_type = "application/vnd.ogc.success+xml";
064: private static final StyleFactory styleFactory = StyleFactoryFinder
065: .createStyleFactory();
066:
067: public PutStyles() {
068: super ("WMS", "PutStyles", null);
069: }
070:
071: protected boolean isServiceEnabled(HttpServletRequest req) {
072: return true;
073: }
074:
075: protected Response getResponseHandler() {
076: return new PutStylesResponse();
077: }
078:
079: protected KvpRequestReader getKvpReader(Map params) {
080: return new PutStylesKvpReader(params, this );
081: }
082:
083: protected XmlRequestReader getXmlRequestReader() {
084: /**
085: * @todo Implement this org.vfny.geoserver.servlets.AbstractService
086: * abstract method
087: */
088: throw new java.lang.UnsupportedOperationException(
089: "Method getXmlRequestReader() not yet implemented.");
090: }
091:
092: protected ExceptionHandler getExceptionHandler() {
093: // TODO Auto-generated method stub
094: /**
095: * @todo Implement this org.vfny.geoserver.servlets.AbstractService
096: * abstract method
097: */
098: throw new java.lang.UnsupportedOperationException(
099: "Method getXmlRequestReader() not yet implemented.");
100: }
101:
102: /**
103: * doGet:
104: *
105: *
106: */
107: public void doGet(HttpServletRequest request,
108: HttpServletResponse response) throws ServletException,
109: IOException {
110: LOGGER.info("PutStyles.doGet()");
111:
112: Map requestParams = new HashMap();
113: String paramName;
114: String paramValue;
115:
116: // gather the parameters
117: for (Enumeration pnames = request.getParameterNames(); pnames
118: .hasMoreElements();) {
119: paramName = (String) pnames.nextElement();
120: paramValue = request.getParameter(paramName);
121: requestParams.put(paramName.toUpperCase(), paramValue);
122: }
123:
124: PutStylesKvpReader requestReader = new PutStylesKvpReader(
125: requestParams, this );
126:
127: PutStylesRequest serviceRequest; // the request object we will deal with
128:
129: try {
130: serviceRequest = (PutStylesRequest) requestReader
131: .getRequest(request);
132: } catch (ServiceException e) {
133: e.printStackTrace();
134:
135: return;
136: }
137:
138: ServletContext context = request.getSession()
139: .getServletContext();
140:
141: try {
142: processSLD(serviceRequest, request, response, context);
143: } catch (SldException e) {
144: throw new ServletException(e);
145: } catch (IOException e) {
146: throw new ServletException(e);
147: }
148: }
149:
150: /**
151: * doPost:
152: *
153: *
154: */
155: public void doPost(HttpServletRequest request,
156: HttpServletResponse response, Reader requestXml)
157: throws ServletException, IOException {
158: LOGGER.fine("PutStyles POST");
159:
160: if (requestXml == null) {
161: requestXml = new BufferedReader(XmlCharsetDetector
162: .getCharsetAwareReader(request.getInputStream()));
163: }
164:
165: File temp = File.createTempFile("putStylesPost", "xml");
166: temp.deleteOnExit();
167:
168: FileOutputStream fos = new FileOutputStream(temp);
169: BufferedOutputStream out = new BufferedOutputStream(fos);
170: StringBuffer sb = new StringBuffer();
171:
172: if (requestXml == null) {
173: throw new NullPointerException();
174: }
175:
176: int c;
177:
178: while (-1 != (c = requestXml.read())) {
179: char chr = (char) c;
180: out.write(c);
181: sb.append(chr);
182: }
183:
184: requestXml.close();
185: out.flush();
186: out.close();
187: requestXml = new BufferedReader(new FileReader(temp)); // pretend like nothing has happened
188:
189: PutStylesRequest serviceRequest = new PutStylesRequest(this );
190: serviceRequest.setSldBody(sb.toString()); // save the SLD body in the request object
191:
192: ServletContext context = request.getSession()
193: .getServletContext();
194:
195: try {
196: processSLD(serviceRequest, request, response, context);
197: } catch (SldException e) {
198: throw new ServletException(e);
199: } catch (IOException e) {
200: throw new ServletException(e);
201: }
202: }
203:
204: private Node generateDOM(Reader reader) {
205: javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory
206: .newInstance();
207:
208: dbf.setExpandEntityReferences(false);
209: dbf.setValidating(false);
210: dbf.setNamespaceAware(true);
211:
212: javax.xml.parsers.DocumentBuilder db;
213: Node rootNode = null;
214:
215: try {
216: db = dbf.newDocumentBuilder();
217:
218: InputSource input = new InputSource(reader);
219: org.w3c.dom.Document dom = db.parse(input);
220:
221: rootNode = dom.getDocumentElement();
222: } catch (ParserConfigurationException e) {
223: e.printStackTrace();
224: } catch (SAXException e) {
225: e.printStackTrace();
226: } catch (IOException e) {
227: e.printStackTrace();
228: }
229:
230: return rootNode;
231: }
232:
233: /**
234: * Give a node and the name of a child of that node, return it. This doesnt
235: * do anything complex.
236: *
237: * @param parentNode
238: * @param wantedChildName
239: *
240: * @return
241: */
242: public Node getNode(Node parentNode, String wantedChildName) {
243: NodeList children = parentNode.getChildNodes();
244:
245: for (int i = 0; i < children.getLength(); i++) {
246: Node child = children.item(i);
247:
248: if ((child == null)
249: || (child.getNodeType() != Node.ELEMENT_NODE)) {
250: continue;
251: }
252:
253: String childName = child.getLocalName();
254:
255: if (childName == null) {
256: childName = child.getNodeName();
257: }
258:
259: if (childName.equalsIgnoreCase(wantedChildName)) {
260: return child;
261: }
262: }
263:
264: return null;
265: }
266:
267: /**
268: * Convenience method to get the value from the specified node.
269: *
270: * @param node
271: * @return
272: */
273: public String getNodeValue(Node node) {
274: return node.getChildNodes().item(0).getNodeValue();
275: }
276:
277: /**
278: * Give a node and the name of a child of that node, find its (string)
279: * value. This doesnt do anything complex.
280: *
281: * @param parentNode
282: * @param wantedChildName
283: *
284: * @return
285: */
286: public String getNodeChildValue(Node parentNode,
287: String wantedChildName) {
288: NodeList children = parentNode.getChildNodes();
289:
290: for (int i = 0; i < children.getLength(); i++) {
291: Node child = children.item(i);
292:
293: if ((child == null)
294: || (child.getNodeType() != Node.ELEMENT_NODE)) {
295: continue;
296: }
297:
298: String childName = child.getLocalName();
299:
300: if (childName == null) {
301: childName = child.getNodeName();
302: }
303:
304: if (childName.equalsIgnoreCase(wantedChildName)) {
305: return child.getChildNodes().item(0).getNodeValue();
306: }
307: }
308:
309: return null;
310: }
311:
312: /**
313: * returns true if this node is named "name". Ignores case and namespaces.
314: *
315: * @param n
316: * @param name
317: *
318: * @return
319: */
320: public boolean nodeNameEqual(Node n, String name) {
321: if (n.getNodeName().equalsIgnoreCase(name)) {
322: return true;
323: }
324:
325: String nname = n.getNodeName();
326: int idx = nname.indexOf(':');
327:
328: if (idx == -1) {
329: return false;
330: }
331:
332: if (nname.substring(idx + 1).equalsIgnoreCase(name)) {
333: return true;
334: }
335:
336: return false;
337: }
338:
339: /**
340: * processSLD:
341: *
342: * Makes the SLD into a DOM object and validates it.
343: * It will then get the layer names and update for each layer.
344: *
345: * @param sld
346: * @param rootNode the root node of the DOM document for parsing
347: * @param response
348: * @throws IOException
349: * @throws WmsException
350: */
351: private void processSLD(PutStylesRequest serviceRequest,
352: HttpServletRequest request, HttpServletResponse response,
353: ServletContext context) throws IOException, SldException {
354: LOGGER.info("Processing SLD");
355:
356: String sld_remote = serviceRequest.getSLD();
357:
358: if ((sld_remote != null) && !sld_remote.equals("")) {
359: throw new java.lang.UnsupportedOperationException(
360: "SLD= param not yet implemented. Use SLD_BODY=");
361: }
362:
363: String sld_body = serviceRequest.getSldBody(); // the actual SLD body
364:
365: if ((sld_body == null) || (sld_body == "")) {
366: throw new IllegalArgumentException(
367: "The body of the SLD cannot be empty!");
368: }
369:
370: // write out SLD so we can read it in and validate it
371: File temp = File.createTempFile("putStyles", "xml");
372: temp.deleteOnExit();
373:
374: FileOutputStream fos = new FileOutputStream(temp);
375: BufferedOutputStream tempOut = new BufferedOutputStream(fos);
376:
377: byte[] bytes = sld_body.getBytes();
378:
379: for (int i = 0; i < bytes.length; i++) {
380: tempOut.write(bytes[i]);
381: }
382:
383: tempOut.flush();
384: tempOut.close();
385:
386: BufferedInputStream fs = new BufferedInputStream(
387: new FileInputStream(temp));
388:
389: // finish making our tempory file stream (for SLD validation)
390: CharArrayReader xml = new CharArrayReader(sld_body
391: .toCharArray()); // put the xml into a 'Reader'
392:
393: Node rootNode = generateDOM(xml);
394:
395: // validate the SLD
396: SLDValidator validator = new SLDValidator();
397: List errors = validator.validateSLD(fs, context);
398:
399: if (errors.size() != 0) {
400: throw new SldException(SLDValidator.getErrorMessage(xml,
401: errors));
402: }
403:
404: Node n_namedLayer = getNode(rootNode, "NamedLayer");
405: Node n_layerName = getNode(n_namedLayer, "Name");
406: Node n_userStyle = getNode(n_namedLayer, "UserStyle");
407: Node n_styleName = getNode(n_userStyle, "Name");
408:
409: // "ftname_styleLayerName": ignore "_style"
410: String layerName = getNodeValue(n_layerName); //.split("_styleLayerName")[0];
411: String styleName = getNodeValue(n_styleName);
412: LOGGER.info("PutStyles SLD:\nLayer: " + layerName + ", style: "
413: + styleName);
414:
415: // store the SLD
416: StyleConfig style = new StyleConfig();
417: style.setId(styleName);
418:
419: // make the SLD file in the data_dir/styles directory
420: File data_dir = GeoserverDataDirectory
421: .getGeoserverDataDirectory();
422: File style_dir;
423:
424: try {
425: style_dir = GeoserverDataDirectory.findConfigDir(data_dir,
426: "styles");
427: } catch (ConfigurationException cfe) {
428: LOGGER.warning("no style dir found, creating new one");
429: //if for some bizarre reason we don't fine the dir, make a new one.
430: style_dir = new File(data_dir, "styles");
431: }
432:
433: File styleFile = new File(style_dir, styleName + ".sld"); // styleName.sld
434: //styleFile.createNewFile();
435:
436: LOGGER.info("Saving new SLD file to " + styleFile.getPath());
437:
438: // populate it with the style code
439: StringBuffer sldText = new StringBuffer();
440: sldText.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
441: sldText.append("<StyledLayerDescriptor version=\"1.0.0\"\n");
442: sldText
443: .append(" xsi:schemaLocation=\"http://www.opengis.net/sld StyledLayerDescriptor.xsd\"\n");
444: sldText
445: .append(" xmlns=\"http://www.opengis.net/sld\" xmlns:ogc=\"http://www.opengis.net/ogc\"\n");
446: sldText
447: .append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
448: sldText
449: .append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
450:
451: FileOutputStream style_fos = new FileOutputStream(styleFile); // save the sld to a file
452:
453: String sldBody = serviceRequest.getSldBody();
454: int start = sldBody.indexOf("<NamedLayer>");
455: int end = sldBody.indexOf("</NamedLayer>");
456:
457: sldText.append(sldBody.substring(start, end));
458: sldText.append("</NamedLayer>\n");
459: sldText.append("</StyledLayerDescriptor>");
460: style_fos.write(sldText.toString().getBytes());
461: style_fos.flush();
462: style_fos.close();
463:
464: style.setFilename(styleFile);
465:
466: // update the data config to tell it about our new style
467: DataConfig dataConfig = ConfigRequests.getDataConfig(request);
468: dataConfig.addStyle(styleName, style);
469:
470: // SLD is set up now, so tell the feature type to use it
471:
472: // get our featureType by the layerName
473: List keys = dataConfig.getFeatureTypeConfigKeys();
474: Iterator it = keys.iterator();
475: layerName = null;
476:
477: while (it.hasNext()) // get the full featureType name that has the datastore prefix
478: {
479: String o = it.next().toString();
480: String[] os = o.split(":");
481:
482: if (os[1].equalsIgnoreCase(layerName)) {
483: layerName = o;
484:
485: break;
486: }
487: }
488:
489: // get the feature type and save the style for it, if the feature type exists yet
490: // If there is no FT there that may mean that the user is just creating it.
491: if (layerName != null) {
492: FeatureTypeConfig featureTypeConfig = dataConfig
493: .getFeatureTypeConfig(layerName);
494: featureTypeConfig.setDefaultStyle(styleName);
495: }
496:
497: // if successful, return "success"
498: //response.setContentType(success_mime_type);
499: LOGGER.info("sending back result");
500:
501: String message = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
502: + "<sld:success>success</sld:success>";
503: BufferedOutputStream out = new BufferedOutputStream(response
504: .getOutputStream());
505: byte[] msg = message.getBytes();
506: out.write(msg);
507: out.flush();
508: }
509: }
|