Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libRemoteServices / src / org / gvsig / remoteClient / wms / WMSProtocolHandler.java @ 5536

History | View | Annotate | Download (21.2 KB)

1
package org.gvsig.remoteClient.wms;
2

    
3
import java.io.ByteArrayInputStream;
4
import java.io.DataInputStream;
5
import java.io.File;
6
import java.io.FileInputStream;
7
import java.io.IOException;
8
import java.io.InputStream;
9
import java.io.StringReader;
10
import java.net.URL;
11
import java.net.URLConnection;
12
import java.nio.ByteBuffer;
13
import java.nio.channels.FileChannel;
14
import java.util.ArrayList;
15
import java.util.HashMap;
16
import java.util.TreeMap;
17
import java.util.Vector;
18

    
19
import org.gvsig.remoteClient.exceptions.ServerErrorException;
20
import org.gvsig.remoteClient.exceptions.WMSException;
21
import org.gvsig.remoteClient.utils.CapabilitiesTags;
22
import org.gvsig.remoteClient.utils.ExceptionTags;
23
import org.gvsig.remoteClient.utils.Utilities;
24
import org.kxml2.io.KXmlParser;
25
import org.xmlpull.v1.XmlPullParserException;
26

    
27
/**
28
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.
29
 * </p>
30
 * 
31
 */
32
public abstract class WMSProtocolHandler {
33
        /**
34
         * Encoding used to parse different xml documents.
35
         */
36
        protected String encoding = "UTF-8";
37
        /**
38
         * procotol handler name
39
         */
40
    protected String name;
41
    /**
42
     * protocol handler version
43
     */
44
    protected String version;
45
    /**
46
     * host of the WMS to connect
47
     */
48
    protected String host;
49
    /**
50
     * port number of the comunication channel of the WMS to connect
51
     */
52
    protected String port;    
53
    /**
54
     * WMS metadata
55
     */
56
    protected ServiceInformation serviceInfo;
57
    public TreeMap layers;
58
    public WMSLayer rootLayer;
59
//    public Vector srs;
60
    
61
    /**
62
     * parses the data retrieved by the WMS in XML format. 
63
     * It will be mostly the WMS Capabilities, but the implementation
64
     * will be placed in the handler implementing certain version of the protocol.
65
     * 
66
     */
67
    public abstract void parse(File f) ;
68
    
69
    /**
70
     * returns the alfanumeric information of the layers at the specified point.
71
     * the diference between the other getfeatureInfo method is that this will
72
     * be implemented by each specific version because the XML from the server will be
73
     * parsed and presented by a well known structure.
74
     */    
75

    
76
    public String getName() {        
77
            return name;
78
    } 
79

    
80
    public String getVersion() {        
81
            return version;
82
    }    
83
    
84
    public ServiceInformation getServiceInformation() {        
85
        return serviceInfo;
86
    }  
87
    public String getHost ()
88
    {
89
            return host;
90
    }
91
    public void setHost(String _host)
92
    {
93
            host = _host;
94
    }
95
    public String getPort()
96
    {
97
            return port;
98
    }
99
    public void setPort(String _port)
100
    {
101
            port = _port;
102
    }
103
    
104

    
105
    /**
106
         * <p>Builds a GetCapabilities request that is sent to the WMS
107
         * the response will be parse to extract the data needed by the
108
         * WMS client</p>
109
         * @param override, if true the previous downloaded data will be overridden
110
         */
111
    public void getCapabilities(WMSStatus status, boolean override, ICancellable cancel)
112
    {                
113
            URL request = null;
114
                try
115
                {
116
                        request = new URL(buildCapabilitiesRequest(status));
117
                }
118
                catch(Exception e)
119
                {
120
                        e.printStackTrace();
121
                }
122
                try
123
                {
124
                        if (override)
125
                                Utilities.removeURL(request);
126
                        File f = Utilities.downloadFile(request,"wms_capabilities.xml", cancel);
127
                        if (f == null)
128
                                return;
129
                        clear();
130
                        parse(f);
131
            } catch(Exception e)
132
                {
133
                        //TODO
134
                        e.printStackTrace();
135
                }
136
    }
137

    
138
    private void clear() {
139
                layers.clear();
140
                serviceInfo.clear();
141
        }
142

    
143
        /**
144
     * <p>It will send a GetFeatureInfo request to the WMS
145
     * Parsing the response and redirecting the info to the WMS client</p>
146
     */
147
    public String getFeatureInfo(WMSStatus status, int x, int y, int featureCount)
148
    {
149
            URL request = null;
150
            StringBuffer output = new StringBuffer();
151
            String outputFormat = new String();
152
            String ServiceException = "ServiceExceptionReport";                        
153
            StringBuffer sb = new StringBuffer();
154
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
155
                try
156
                {
157
                        //TODO:
158
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
159
                        request = new URL(buildGetFeatureInfoRequest(status, x, y));
160
                        
161
                        //TODO:
162
                        //see which output format is being requested.                                         
163
                }
164
                catch(Exception e)
165
                {
166
                        e.printStackTrace();
167
                        return "";
168
                }
169
                
170
            try
171
            {                                
172
                    System.out.println(request.toString());          
173
                    byte[] buffer = new byte[1024*256];                    
174
                    DataInputStream is = new DataInputStream(request.openStream());                    
175
                    outputFormat = request.openConnection().getContentType();
176

    
177
                    for (int i = is.read(buffer); i>0; i = is.read(buffer))
178
                    {
179
                            String str = new String(buffer,0,i);
180
                            output.append(str);                    
181
                    }
182
                                
183
                    is.close();
184
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
185
                                    ||output.toString().toLowerCase().startsWith("<?xml")
186
                                    ||(outputFormat.indexOf("gml") != -1))
187
                    {
188
                            int tag;
189
                            KXmlParser kxmlParser = null;
190
                            kxmlParser = new KXmlParser();            
191
                            kxmlParser.setInput(new StringReader(output.toString()));
192
                            
193
                            tag = kxmlParser.nextTag();           
194
                            if (kxmlParser.getName().compareTo(ServiceException)==0)
195
                                {
196
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
197
                                    return sb.toString();                                                                        
198
                                }
199
                                else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
200
                                {
201
                                        return output.toString();
202
                                }
203
                                else                
204
                                {
205
                                        return output.toString();                
206
                                }
207
                                                            
208
//                                  while(tag != KXmlParser.END_DOCUMENT)
209
//                                 {
210
//                                         switch(tag)
211
//                                         {
212
//                                                case KXmlParser.START_TAG:                
213
//                                                        if (kxmlParser.getName().compareTo(ServiceException)==0)
214
//                                                        {
215
//                                                            sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
216
//                                                            return sb.toString();                                                                        
217
//                                                        }
218
//                                                        else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
219
//                                                                return output.toString();
220
//                                                        else                                                                
221
//                                                                sb.append("<" + kxmlParser.getName() + ">\n");                                                        
222
//                                                        break;
223
//                                                case KXmlParser.END_TAG:        
224
//                                                        sb.append("</" + kxmlParser.getName() + ">\n");
225
//                                                        break;
226
//                                                case KXmlParser.TEXT:
227
//                                                        sb.append(kxmlParser.getText());                                                
228
//                                                break;
229
//                                         }
230
//                                     tag = kxmlParser.next();
231
//                             }                                    
232
                            //return sb.toString();
233
                    }
234
                    else
235
                    {
236
                            sb.append("<INFO>").append("Info format not supported").append("</INFO>");
237
                            return sb.toString();
238
                            //Para que funcione con el GetFeatureInfo Viewer generico hay que devolver:
239
                            // return output.toString();
240
                    }
241
                }
242
            catch(XmlPullParserException parserEx)
243
            {
244
                    if (output.toString().toLowerCase().indexOf("xml") != -1)
245
                    {
246
                            return output.toString().trim();
247
                    }
248
                    else
249
                    {
250
                               sb.append("<INFO>").append("Info format not supported").append("</INFO>");
251
                        return sb.toString();
252
                    }
253
            }
254
            catch(Exception e)
255
            {
256
                    e.printStackTrace();
257
                    sb.append("<INFO>").append("Info format not supported").append("</INFO>");
258
                    return sb.toString();
259

    
260
            }
261
    }
262
    /**
263
     * <p>Builds a GetMap request that is sent to the WMS
264
     * the response (image) will be redirect to the
265
     * WMS client</p>
266
     */   
267
    public byte[] _getMap(WMSStatus status) throws ServerErrorException, WMSException
268
    {        
269
            URL request = null;
270
                try
271
                {
272
                        //TODO:
273
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
274
                        request = new URL(buildMapRequest(status));
275
                        URLConnection conn = request.openConnection();
276
                        System.out.println(request.toString());
277
            String type = conn.getContentType();
278
            
279
                                
280
                    byte[] imageBytes = null;
281
                    byte[] buffer = new byte[1024*256];
282
            InputStream is = conn.getInputStream();
283
                    int readed = 0;
284
                    
285
                    for (int i = is.read(buffer); i>0; i = is.read(buffer)){
286
                // Creates a new buffer to contain the previous readed bytes and the next bunch of bytes
287
                            byte[] buffered = new byte[readed+i];
288
                            for (int j = 0; j < buffered.length; j++) {
289
                                    if (j<readed){
290
                        // puts the previously downloaded bytes into the image buffer
291
                                            buffered[j] = imageBytes[j];
292
                                    }
293
                                    else {
294
                        // appends the recently downloaded bytes to the image buffer.
295
                                            buffered[j] = buffer[j-readed];
296
                                    }
297
                                }
298
                            imageBytes = (byte[]) buffered.clone();
299
                            readed += i;                            
300
                    }
301
                    
302
                    if ((type !=null && !type.subSequence(0,5).equals("image")) 
303
                            ||(Utilities.isTextData(imageBytes)))
304
                    {                            
305
                       WMSException wmsEx = null;
306
                       
307
                    String exceptionMessage = parseException(imageBytes);
308
                if (exceptionMessage==null)
309
                {
310
                         String error = new String(imageBytes);
311
                        int pos = error.indexOf("<?xml");
312
                        if (pos!= -1)
313
                        {
314
                                String xml = error.substring(pos,error.length());
315
                                exceptionMessage = parseException(xml.getBytes());
316
                        if (exceptionMessage == null)
317
                                exceptionMessage = new String(imageBytes);
318
                        }
319
                }
320
                     wmsEx = new WMSException(exceptionMessage);
321
                    wmsEx.setWMSMessage(new String(imageBytes));
322
                throw wmsEx;
323
            }
324
                        return imageBytes;                    
325
                }
326
                catch(IOException e)
327
                {
328
                        e.printStackTrace();
329
            throw new ServerErrorException();
330
                }
331
    } 
332
    
333
    public File getMap(WMSStatus status, ICancellable cancel) throws ServerErrorException, WMSException
334
    {        
335
            URL request = null;
336
                try
337
                {
338
                        //TODO:
339
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
340
                        request = new URL(buildMapRequest(status));
341
            
342
            File f = Utilities.downloadFile(request, "wmsGetMap", cancel);                                        
343
                    if (f== null)
344
                            return f;
345
            if (Utilities.isTextFile(f)) {
346
                            FileInputStream fis = new FileInputStream(f);
347
                            FileChannel fc = fis.getChannel();
348
                            byte[] data = new byte[(int)fc.size()];   // fc.size returns the size of the file which backs the channel
349
                            ByteBuffer bb = ByteBuffer.wrap(data);
350
                            fc.read(bb);
351
                                                        
352
                            WMSException wmsEx = null;
353
                       
354
                    String exceptionMessage = parseException(data);
355
                if (exceptionMessage==null)
356
                {
357
                         String error = new String(data);
358
                        int pos = error.indexOf("<?xml");
359
                        if (pos!= -1)
360
                        {
361
                                String xml = error.substring(pos,error.length());
362
                                exceptionMessage = parseException(xml.getBytes());
363
//                        if (exceptionMessage == null)
364
//                                exceptionMessage = new String(data);
365
                        }               
366
                    if (exceptionMessage == null)
367
                            exceptionMessage = new String(data);
368
                        
369
                }
370
                     wmsEx = new WMSException(exceptionMessage);
371
                    wmsEx.setWMSMessage(new String(data));
372
                    
373
                    // Since it is an error file, It must be deleted from the cache
374
                    Utilities.removeURL(request);
375
                throw wmsEx;
376
            }
377
                        return f;                    
378
                }
379
                catch(IOException e)
380
                {
381
                        e.printStackTrace();
382
            throw new ServerErrorException();
383
                }
384
    } 
385
    
386
    
387
    /* (non-Javadoc)
388
     * @see org.gvsig.remoteClient.wms.WMSProtocolHandler#parseException(byte[])
389
     */
390
    protected String parseException(byte[] data) {
391
        ArrayList errors = new ArrayList();
392
        KXmlParser kxmlParser = new KXmlParser();
393
        try
394
        {
395
            kxmlParser.setInput(new ByteArrayInputStream(data), encoding);        
396
            kxmlParser.nextTag();
397
            int tag;
398
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT ) 
399
            { 
400
                kxmlParser.require(KXmlParser.START_TAG, null, ExceptionTags.EXCEPTION_ROOT);             
401
                tag = kxmlParser.nextTag();
402
                 while(tag != KXmlParser.END_DOCUMENT)
403
                 {
404
                     switch(tag)
405
                     {
406
                        case KXmlParser.START_TAG:
407
                            if (kxmlParser.getName().compareTo(ExceptionTags.SERVICE_EXCEPTION)==0){
408
                                String errorCode = kxmlParser.getAttributeValue("", ExceptionTags.CODE);
409
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
410
                                String errorMessage = kxmlParser.nextText();
411
                                errors.add(errorCode+errorMessage);
412
                            }
413
                            break;
414
                        case KXmlParser.END_TAG:                            
415
                            break;
416
                        
417
                     }
418
                     tag = kxmlParser.nextTag();
419
                 }
420
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
421
            }
422
        }
423
        catch(XmlPullParserException parser_ex){ 
424
            parser_ex.printStackTrace();
425
        }
426
        catch (IOException ioe) {           
427
            ioe.printStackTrace();            
428
        }
429
        String message = errors.size()>0? "" : null;
430
        for (int i = 0; i < errors.size(); i++) {
431
            message += (String) errors.get(i)+"\n";
432
        }
433
        return message;
434
    }
435
    /**
436
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
437
     * without a VERSION, to get the highest version than a WMS supports.
438
     */
439
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version)
440
    {
441
                String req = new String();                
442
        String symbol = getSymbol(_host);
443
        req = req + _host + symbol + "REQUEST=GetCapabilities&SERVICE=WMS&";                
444
        if((_version != null) && (_version.length()>0 ))
445
        {
446
                req += ("&VERSION=" + _version);
447
        }
448
                req += ("&EXCEPTIONS=XML");
449
                return req;           
450
    }
451
    
452
    /**
453
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
454
     * @param WMSStatus
455
     */
456
    private String buildCapabilitiesRequest(WMSStatus status)
457
    {
458
                StringBuffer req = new StringBuffer();
459
                String symbol = null;
460
                
461
                String onlineResource;
462
                if (status == null || status.getOnlineResource() == null)
463
                        onlineResource = getHost();
464
                else 
465
                        onlineResource = status.getOnlineResource();
466
                symbol = getSymbol(onlineResource);
467
                
468
                req.append(onlineResource).append(symbol).append("REQUEST=GetCapabilities&SERVICE=WMS&");
469
                req.append("VERSION=").append(getVersion()).append("&EXCEPTIONS=XML");
470
                return req.toString();
471
    }
472
    
473
    /**
474
     * Builds the GetFeatureInfoRequest according to the OGC WMS Specifications
475
     */
476
    protected String buildGetFeatureInfoRequest(WMSStatus status, int x, int y)
477
    {
478
                StringBuffer req = new StringBuffer();
479
                String symbol = null;
480

    
481
                String onlineResource;
482
                if (status.getOnlineResource() == null)
483
                        onlineResource = getHost();
484
                else 
485
                        onlineResource = status.getOnlineResource();
486
                symbol = getSymbol(onlineResource);
487
        
488
                req.append(onlineResource).append(symbol).append("REQUEST=GetFeatureInfo&SERVICE=WMS&");
489
                req.append("QUERY_LAYERS=").append(Utilities.Vector2CS(status.getLayerNames())); 
490
                req.append("&VERSION=").append(getVersion()).append("&INFO_FORMAT=application/vnd.ogc.gml&");
491
                req.append(getPartialQuery(status)).append("&x="+x + "&y="+y);
492
       if (status.getExceptionFormat() != null) {
493
            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
494
        } else {
495
            req.append("&EXCEPTIONS=XML");
496
        }
497
                return req.toString().replaceAll(" ", "%20");
498
    }    
499

    
500
    /**
501
     * Builds the GetMapRequest according to the OGC WMS Specifications
502
     */
503
    private String buildMapRequest(WMSStatus status)
504
    { 
505
                StringBuffer req = new StringBuffer();
506
                String symbol = null;
507
                String onlineResource = null;
508
                
509
                if (status.getOnlineResource() == null)
510
                        onlineResource = getHost();
511
                else 
512
                        onlineResource = status.getOnlineResource();
513
                symbol = getSymbol(onlineResource);
514
        
515
                req.append(onlineResource + symbol + "REQUEST=GetMap&SERVICE=WMS&VERSION=").append(getVersion()).append("&");
516
                req.append(getPartialQuery(status));
517
//        if (status.getExceptionFormat() != null) {
518
//            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
519
//        } else {
520
//            req.append("&EXCEPTIONS=XML");
521
//        }
522
                return req.toString().replaceAll(" ", "%20");
523
    }
524
    
525
    /**
526
     * Just for not repeat code. Gets the correct separator according to the server URL
527
     * @param h
528
     * @return
529
     */
530
    private static String getSymbol(String h) {
531
        String symbol;
532
        if (h.indexOf("?")==-1) 
533
            symbol = "?";
534
        else if (h.indexOf("?")!=h.length()-1)
535
            symbol = "&";
536
        else
537
            symbol = "";
538
        return symbol;
539
    }
540

    
541
    /**
542
     * Gets the part of the OGC request that share GetMap and GetFeatureInfo
543
     * @return String request
544
     */
545
    public String getPartialQuery(WMSStatus status)
546
    {            
547
        StringBuffer req = new StringBuffer();
548
        req.append("LAYERS=" + Utilities.Vector2CS(status.getLayerNames()))
549
           .append("&SRS=" + status.getSrs())
550
           .append("&BBOX=" + status.getExtent().getMinX()+ "," )
551
           .append(status.getExtent().getMinY()+ ",")
552
           .append(status.getExtent().getMaxX()+ ",")
553
           .append(status.getExtent().getMaxY())
554
           .append("&WIDTH=" + status.getWidth())
555
           .append("&HEIGHT=" + status.getHeight())
556
           .append("&FORMAT=" + status.getFormat())
557
           .append("&STYLES=");
558
        Vector v = status.getStyles();
559
        if (v!=null && v.size()>0)
560
                req.append(Utilities.Vector2CS(v)); 
561
        v = status.getDimensions();
562
        if (v!=null && v.size()>0)
563
            req.append("&" + Utilities.Vector2URLParamString(v));
564
        if (status.getTransparency()) {
565
            req.append("&TRANSPARENT=TRUE");
566
        }
567
        return req.toString();
568
    }
569

    
570
    
571
    
572
    public void close() {        
573
        // your code here
574
    } 
575
    
576
    /**
577
     * Inner class that represents the description of the WMS metadata.
578
     * The first part of the capabilities will return the service information
579
     * from the WMS, this class will hold this information. 
580
     * 
581
     */
582
    public class ServiceInformation {
583

    
584
        public String online_resource = null;
585
        /*public String map_online_resource = null;
586
        public String feature_online_resource = null;*/
587
        public String version;
588
        public String name;
589
        public String scope;
590
        public String title;
591
        public String abstr;
592
        public String keywords;
593
        public String fees;
594
        public String operationsInfo;
595
        public String personname;
596
        public String organization;
597
        public String function;
598
        public String addresstype;
599
        public String address;
600
        public String place;
601
        public String province;
602
        public String postcode;
603
        public String country;
604
        public String phone;
605
        public String fax;
606
        public String email;
607
        public Vector formats;
608
        public HashMap operations; // operations that WMS supports
609
        
610
        public ServiceInformation()
611
        {          
612
            version = new String();
613
            name = new String();
614
            scope = new String();
615
            title = new String();
616
            abstr = new String();
617
            keywords = new String();
618
            fees = new String();
619
            operationsInfo = new String();
620
            personname = new String();
621
            organization = new String();
622
            function = new String();
623
            addresstype = new String();
624
            address = new String();
625
            place = new String();
626
            province = new String();
627
            postcode = new String();
628
            country = new String();
629
            phone = new String();
630
            fax = new String();
631
            email = new String();
632
            formats = new Vector();               
633
            operations = new HashMap();            
634
        }
635
        public boolean isQueryable()
636
        {
637
                if (operations.keySet().contains( CapabilitiesTags.GETFEATUREINFO ))
638
                        return true;
639
                else
640
                        return false;
641
        }
642
        
643
        public void clear() {
644
                version = new String();
645
            name = new String();
646
            scope = new String();
647
            title = new String();
648
            abstr = new String();
649
            keywords = new String();
650
            fees = new String();
651
            operationsInfo = new String();
652
            personname = new String();
653
            organization = new String();
654
            function = new String();
655
            addresstype = new String();
656
            address = new String();
657
            place = new String();
658
            province = new String();
659
            postcode = new String();
660
            country = new String();
661
            phone = new String();
662
            fax = new String();
663
            email = new String();
664
            formats = new Vector();               
665
            operations = new HashMap();            
666
        }
667
     }   
668
 }