Statistics
| Revision:

root / trunk / libraries / libRemoteServices / src / org / gvsig / remoteClient / wms / WMSProtocolHandler.java @ 4293

History | View | Annotate | Download (19.8 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.InputStreamReader;
10
import java.io.Reader;
11
import java.io.StringReader;
12
import java.net.URL;
13
import java.net.URLConnection;
14
import java.nio.ByteBuffer;
15
import java.nio.channels.FileChannel;
16
import java.util.ArrayList;
17
import java.util.HashMap;
18
import java.util.TreeMap;
19
import java.util.Vector;
20

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

    
29
import com.iver.andami.PluginServices;
30

    
31
/**
32
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.
33
 * </p>
34
 * 
35
 */
36
public abstract class WMSProtocolHandler {
37
        
38
        /**
39
         * procotol handler name
40
         */
41
    protected String name;
42
    /**
43
     * protocol handler version
44
     */
45
    protected String version;
46
    /**
47
     * host of the WMS to connect
48
     */
49
    protected String host;
50
    /**
51
     * port number of the comunication channel of the WMS to connect
52
     */
53
    protected String port;    
54
    /**
55
     * WMS metadata
56
     */
57
    protected ServiceInformation serviceInfo;
58
    public TreeMap layers;
59
    public WMSLayer rootLayer;
60
    public Vector srs;
61
    
62
    /**
63
     * parses the data retrieved by the WMS in XML format. 
64
     * It will be mostly the WMS Capabilities, but the implementation
65
     * will be placed in the handler implementing certain version of the protocol.
66
     * 
67
     */
68
    public abstract void parse(File f) ;
69

    
70
    public String getName() {        
71
            return name;
72
    } 
73

    
74
    public String getVersion() {        
75
            return version;
76
    }    
77
    
78
    public ServiceInformation getServiceInformation() {        
79
        return serviceInfo;
80
    }  
81
    public String getHost ()
82
    {
83
            return host;
84
    }
85
    public void setHost(String _host)
86
    {
87
            host = _host;
88
    }
89
    public String getPort()
90
    {
91
            return port;
92
    }
93
    public void setPort(String _port)
94
    {
95
            port = _port;
96
    }
97
    
98

    
99
    /**
100
         * <p>Builds a GetCapabilities request that is sent to the WMS
101
         * the response will be parse to extract the data needed by the
102
         * WMS client</p>
103
         */
104
    public void getCapabilities(WMSStatus status)
105
    {                
106
            URL request = null;
107
                try
108
                {
109
                        request = new URL(buildCapabilitiesRequest(status));
110
                }
111
                catch(Exception e)
112
                {
113
                        e.printStackTrace();
114
                }
115
                try
116
                {
117
                File f = com.iver.andami.Utilities.downloadFile(request,"wms_capabilities.xml");
118
            parse(f);
119
            } catch(Exception e)
120
                {
121
                        //TODO
122
                        e.printStackTrace();
123
                }
124
    }
125

    
126
    /**
127
     * <p>It will send a GetFeatureInfo request to the WMS
128
     * Parsing the response and redirecting the info to the WMS client</p>
129
     */
130
    public String getFeatureInfo(WMSStatus status, int x, int y, int featureCount)
131
    {
132
            URL request = null;
133
            StringBuffer output = new StringBuffer();
134
            String outputFormat = new String();
135
            String ServiceException = "ServiceExceptionReport";                        
136
            StringBuffer sb = new StringBuffer();
137
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
138
                try
139
                {
140
                        //TODO:
141
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
142
                        request = new URL(buildGetFeatureInfoRequest(status, x, y));
143
                        
144
                        //TODO:
145
                        //see which output format is being requested.                                         
146
                }
147
                catch(Exception e)
148
                {
149
                        e.printStackTrace();
150
                        return "";
151
                }
152
                
153
            try
154
            {                                
155
                    System.out.println(request.toString());          
156
                    byte[] buffer = new byte[1024*256];                    
157
                    DataInputStream is = new DataInputStream(request.openStream());                    
158
                    outputFormat = request.openConnection().getContentType();
159

    
160
                    for (int i = is.read(buffer); i>0; i = is.read(buffer))
161
                    {
162
                            String str = new String(buffer,0,i);
163
                            output.append(str);                    
164
                    }
165
                                
166
                    is.close();
167
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
168
                                    ||output.toString().toLowerCase().startsWith("<?xml")
169
                                    ||(outputFormat.indexOf("gml") != -1))
170
                    {
171
                            int tag;
172
                            KXmlParser kxmlParser = null;
173
                            kxmlParser = new KXmlParser();            
174
                            kxmlParser.setInput(new StringReader(output.toString()));
175
                            
176
                            tag = kxmlParser.nextTag();           
177
                            if (kxmlParser.getName().compareTo(ServiceException)==0)
178
                                {
179
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
180
                                    return sb.toString();                                                                        
181
                                }
182
                                else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
183
                                {
184
                                        return output.toString();
185
                                }
186
                                else                
187
                                {
188
                                        return output.toString();                
189
                                }
190
                                                            
191
//                                  while(tag != KXmlParser.END_DOCUMENT)
192
//                                 {
193
//                                         switch(tag)
194
//                                         {
195
//                                                case KXmlParser.START_TAG:                
196
//                                                        if (kxmlParser.getName().compareTo(ServiceException)==0)
197
//                                                        {
198
//                                                            sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
199
//                                                            return sb.toString();                                                                        
200
//                                                        }
201
//                                                        else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
202
//                                                                return output.toString();
203
//                                                        else                                                                
204
//                                                                sb.append("<" + kxmlParser.getName() + ">\n");                                                        
205
//                                                        break;
206
//                                                case KXmlParser.END_TAG:        
207
//                                                        sb.append("</" + kxmlParser.getName() + ">\n");
208
//                                                        break;
209
//                                                case KXmlParser.TEXT:
210
//                                                        sb.append(kxmlParser.getText());                                                
211
//                                                break;
212
//                                         }
213
//                                     tag = kxmlParser.next();
214
//                             }                                    
215
                            //return sb.toString();
216
                    }
217
                    else
218
                    {
219
                            sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
220
                            return sb.toString();
221
                    }
222
                }
223
            catch(XmlPullParserException parserEx)
224
            {
225
                    if (output.toString().toLowerCase().indexOf("xml") != -1)
226
                    {
227
                            return output.toString().trim();
228
                    }
229
                    else
230
                    {
231
                               sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
232
                        return sb.toString();
233
                    }
234
            }
235
            catch(Exception e)
236
            {
237
                    e.printStackTrace();
238
                    sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
239
                    return sb.toString();
240

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

    
464
                String onlineResource;
465
                if (status.getOnlineResource() == null)
466
                        onlineResource = getHost();
467
                else 
468
                        onlineResource = status.getOnlineResource();
469
                symbol = getSymbol(onlineResource);
470
        
471
                req.append(onlineResource).append(symbol).append("REQUEST=GetFeatureInfo&SERVICE=WMS&");
472
                req.append("QUERY_LAYERS=").append(Utilities.Vector2CS(status.getLayerNames())); 
473
                req.append("&VERSION=").append(getVersion()).append("&INFO_FORMAT=application/vnd.ogc.gml&");
474
                req.append(getPartialQuery(status)).append("&x="+x + "&y="+y);
475
       if (status.getExceptionFormat() != null) {
476
            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
477
        } else {
478
            req.append("&EXCEPTIONS=XML");
479
        }
480
                return req.toString().replaceAll(" ", "%20");
481
    }    
482

    
483
    /**
484
     * Builds the GetMapRequest according to the OGC WMS Specifications
485
     */
486
    private String buildMapRequest(WMSStatus status)
487
    { 
488
                StringBuffer req = new StringBuffer();
489
                String symbol = null;
490
                String onlineResource = null;
491
                
492
                if (status.getOnlineResource() == null)
493
                        onlineResource = getHost();
494
                else 
495
                        onlineResource = status.getOnlineResource();
496
                symbol = getSymbol(onlineResource);
497
        
498
                req.append(onlineResource + symbol + "REQUEST=GetMap&SERVICE=WMS&VERSION=").append(getVersion()).append("&");
499
                req.append(getPartialQuery(status));
500
       if (status.getExceptionFormat() != null) {
501
            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
502
        } else {
503
            req.append("&EXCEPTIONS=XML");
504
        }
505
                return req.toString().replaceAll(" ", "%20");
506
    }
507
    
508
    /**
509
     * Just for not repeat code. Gets the correct separator according to the server URL
510
     * @param h
511
     * @return
512
     */
513
    private static String getSymbol(String h) {
514
        String symbol;
515
        if (h.indexOf("?")==-1) 
516
            symbol = "?";
517
        else if (h.indexOf("?")!=h.length()-1)
518
            symbol = "&";
519
        else
520
            symbol = "";
521
        return symbol;
522
    }
523

    
524
    /**
525
     * Gets the part of the OGC request that share GetMap and GetFeatureInfo
526
     * @return String request
527
     */
528
    public String getPartialQuery(WMSStatus status)
529
    {            
530
        StringBuffer req = new StringBuffer();
531
        req.append("LAYERS=" + Utilities.Vector2CS(status.getLayerNames()))
532
           .append("&SRS=" + status.getSrs())
533
           .append("&BBOX=" + status.getExtent().getMinX()+ "," )
534
           .append(status.getExtent().getMinY()+ ",")
535
           .append(status.getExtent().getMaxX()+ ",")
536
           .append(status.getExtent().getMaxY())
537
           .append("&WIDTH=" + status.getWidth())
538
           .append("&HEIGHT=" + status.getHeight())
539
           .append("&FORMAT=" + status.getFormat())
540
           .append("&STYLES=");
541
        Vector v = status.getStyles();
542
        if (v!=null && v.size()>0)
543
                req.append(Utilities.Vector2CS(v)); 
544
        v = status.getDimensions();
545
        if (v!=null && v.size()>0)
546
            req.append("&" + Utilities.Vector2URLParamString(v));
547
        if (status.getTransparency()) {
548
            req.append("&TRANSPARENT=TRUE");
549
        }
550
        return req.toString();
551
    }
552

    
553
    
554
    
555
    public void close() {        
556
        // your code here
557
    } 
558
    
559
    /**
560
     * Inner class that represents the description of the WMS metadata.
561
     * The first part of the capabilities will return the service information
562
     * from the WMS, this class will hold this information. 
563
     * 
564
     */
565
    public class ServiceInformation {
566

    
567
        public String online_resource = null;
568
        /*public String map_online_resource = null;
569
        public String feature_online_resource = null;*/
570
        public String version;
571
        public String name;
572
        public String scope;
573
        public String title;
574
        public String abstr;
575
        public String keywords;
576
        public String fees;
577
        public String operationsInfo;
578
        public String personname;
579
        public String organization;
580
        public String function;
581
        public String addresstype;
582
        public String address;
583
        public String place;
584
        public String province;
585
        public String postcode;
586
        public String country;
587
        public String phone;
588
        public String fax;
589
        public String email;
590
        public Vector formats;
591
        public HashMap operations; // operations that WMS supports
592
        
593
        public ServiceInformation()
594
        {          
595
            version = new String();
596
            name = new String();
597
            scope = new String();
598
            title = new String();
599
            abstr = new String();
600
            keywords = new String();
601
            fees = new String();
602
            operationsInfo = new String();
603
            personname = new String();
604
            organization = new String();
605
            function = new String();
606
            addresstype = new String();
607
            address = new String();
608
            place = new String();
609
            province = new String();
610
            postcode = new String();
611
            country = new String();
612
            phone = new String();
613
            fax = new String();
614
            email = new String();
615
            formats = new Vector();               
616
            operations = new HashMap();            
617
        }
618
        public boolean isQueryable()
619
        {
620
                if (operations.keySet().contains( CapabilitiesTags.GETFEATUREINFO ))
621
                        return true;
622
                else
623
                        return false;
624
        }
625
     }   
626
 }