Statistics
| Revision:

svn-gvsig-desktop / branches / MULTITHREADING_DEVELOPMENT / libraries / libRemoteServices / src / org / gvsig / remoteClient / wms / WMSProtocolHandler.java @ 5378

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

    
18
import org.gvsig.remoteClient.ICancellable;
19
import org.gvsig.remoteClient.exceptions.ServerErrorException;
20
import org.gvsig.remoteClient.exceptions.WMSException;
21
import org.gvsig.remoteClient.taskplanning.retrieving.RequestManager;
22
import org.gvsig.remoteClient.taskplanning.retrieving.RetrieveEvent;
23
import org.gvsig.remoteClient.taskplanning.retrieving.RetrieveListener;
24
import org.gvsig.remoteClient.taskplanning.retrieving.URLRequest;
25
import org.gvsig.remoteClient.utils.CapabilitiesTags;
26
import org.gvsig.remoteClient.utils.ExceptionTags;
27
import org.gvsig.remoteClient.utils.Utilities;
28
import org.kxml2.io.KXmlParser;
29
import org.xmlpull.v1.XmlPullParserException;
30

    
31
import com.iver.andami.PluginServices;
32

    
33
/**
34
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.
35
 * </p>
36
 * 
37
 */
38
public abstract class WMSProtocolHandler {
39
        /**
40
         * Encoding used to parse different xml documents.
41
         */
42
        protected String encoding = "UTF-8";
43
        /**
44
         * procotol handler name
45
         */
46
    protected String name;
47
    /**
48
     * protocol handler version
49
     */
50
    protected String version;
51
    /**
52
     * host of the WMS to connect
53
     */
54
    protected String host;
55
    /**
56
     * port number of the comunication channel of the WMS to connect
57
     */
58
    protected String port;    
59
    /**
60
     * WMS metadata
61
     */
62
    protected ServiceInformation serviceInfo;
63
    public TreeMap layers;
64
    public WMSLayer rootLayer;
65
    public Vector srs;
66
    
67
        private URLRequest capabilitiesRequest, mapRequest, featureInfo;
68
    
69
    /**
70
     * The listener that a driver must implement to receive the events.
71
     */
72
    private WMSEventListener theListener;
73
    
74
    /**
75
     * Listener for the asynchronous getCapabilities requests
76
     */
77
    private RetrieveListener getCapabilitiesListener = new RetrieveListener() {
78

    
79
                public void transferEventReceived(RetrieveEvent event) {
80
                        int type = event.getType();
81
                        
82
                        if (type == RetrieveEvent.REQUEST_FINISHED) {
83
                                System.out.println("parsing "+capabilitiesRequest.getFileName());
84
                                parse(new File(capabilitiesRequest.getFileName()));
85
                                
86
                        }
87
                        if (theListener!=null) {
88
                                theListener.newEvent(WMSEventListener.CAPABILITIES, event);
89
                        }
90

    
91
                }
92
            
93
    } ,
94
    /**
95
     * Listener for the asynchronous getMap requests
96
     */
97
    getMapListener = new RetrieveListener() {
98

    
99
                public void transferEventReceived(RetrieveEvent event) {
100
                        int type = event.getType();
101
                        
102
                        if (type == RetrieveEvent.REQUEST_FINISHED) {
103
                                
104
                                // TODO revisar no estic segur de que a?? haja d'estar ac?
105
                                System.out.println(event.getFileName());
106
                                
107
                                File f = new File(event.getFileName());
108
                                if (Utilities.isTextFile(f)) {
109
                                        FileInputStream fis;
110
                                        try {
111
                                                fis = new FileInputStream(f);
112
                                                FileChannel fc = fis.getChannel();
113
                                                byte[] data = new byte[(int)fc.size()];   // fc.size returns the size of the file which backs the channel
114
                                                ByteBuffer bb = ByteBuffer.wrap(data);
115
                                                fc.read(bb);
116
                                                String exceptionMessage = parseException(data);
117
                                                if (exceptionMessage==null)
118
                                                {
119
                                                        String error = new String(data);
120
                                                        int pos = error.indexOf("<?xml");
121
                                                        if (pos!= -1)
122
                                                        {
123
                                                                String xml = error.substring(pos,error.length());
124
                                                                exceptionMessage = parseException(xml.getBytes());
125
                                                        }               
126
                                                        if (exceptionMessage == null)
127
                                                                exceptionMessage = new String(data);
128
                                                        
129
                                                }
130
                                                event.setType(RetrieveEvent.ERROR);
131
                                                event.setMessage(exceptionMessage);
132
                                                
133
                                                // Since it is an error file, It must be deleted from the cache
134
                                                RequestManager.getInstance().removeURLRequest(mapRequest);
135
                                        } catch (Exception e) {
136
                                                e.printStackTrace();
137
                                        } 
138
                                        
139
                                        
140
                    }        
141
                                if (theListener!=null) {
142
                                        theListener.newEvent(WMSEventListener.MAP, event);
143
                                }
144
                        }
145
                }
146
            
147
    } ,
148
    getFeatureInfoListener = new RetrieveListener() {
149
                public void transferEventReceived(RetrieveEvent event) {
150
                        int type = event.getType();
151
                        
152
                }};
153
    /**
154
     * parses the data retrieved by the WMS in XML format. 
155
     * It will be mostly the WMS Capabilities, but the implementation
156
     * will be placed in the handler implementing certain version of the protocol.
157
     * 
158
     */
159
    public abstract void parse(File f) ;
160

    
161
    public String getName() {        
162
            return name;
163
    } 
164

    
165
    public String getVersion() {        
166
            return version;
167
    }    
168
    
169
    public ServiceInformation getServiceInformation() {        
170
        return serviceInfo;
171
    }  
172
    public String getHost ()
173
    {
174
            return host;
175
    }
176
    public void setHost(String _host)
177
    {
178
            host = _host;
179
    }
180
    public String getPort()
181
    {
182
            return port;
183
    }
184
    public void setPort(String _port)
185
    {
186
            port = _port;
187
    }
188
    
189

    
190
    /**
191
         * <p>Builds a GetCapabilities request that is sent to the WMS
192
         * the response will be parsed to extract the data needed by the
193
         * WMS client</p>
194
         * @param override, if true the previous downloaded data will be overridden
195
         */
196
    public void getCapabilities(WMSStatus status, boolean override)
197
    {                
198
            
199
                try
200
                {
201
                        //request = new URL(buildCapabilitiesRequest(status));
202
                        capabilitiesRequest = buildCapabilitiesRequest(status);
203
                        capabilitiesRequest.setFileName("wmsCapabilities");
204
                        if (override)
205
                                RequestManager.getInstance().removeURLRequest(capabilitiesRequest);
206
                        capabilitiesRequest = RequestManager.getInstance().addURLRequest(capabilitiesRequest, getCapabilitiesListener);
207
                        
208
            } catch(MalformedURLException e) {
209
                        e.printStackTrace();
210
                }
211
    }
212

    
213
    /**
214
     * <p>It will send a GetFeatureInfo request to the WMS
215
     * Parsing the response and redirecting the info to the WMS client</p>
216
     */
217
    public String getFeatureInfo(WMSStatus status, int x, int y, int featureCount)
218
    {
219
            URL request = null;
220
            StringBuffer output = new StringBuffer();
221
            String outputFormat = new String();
222
            String ServiceException = "ServiceExceptionReport";                        
223
            StringBuffer sb = new StringBuffer();
224
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
225
                try
226
                {
227
                        //TODO:
228
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
229
                        request = new URL(buildGetFeatureInfoRequest(status, x, y));
230
                        
231
                        //TODO:
232
                        //see which output format is being requested.                                         
233
                }
234
                catch(Exception e)
235
                {
236
                        e.printStackTrace();
237
                        return "";
238
                }
239
                
240
            try
241
            {                                
242
                    System.out.println(request.toString());          
243
                    byte[] buffer = new byte[1024*256];                    
244
                    DataInputStream is = new DataInputStream(request.openStream());                    
245
                    outputFormat = request.openConnection().getContentType();
246

    
247
                    for (int i = is.read(buffer); i>0; i = is.read(buffer))
248
                    {
249
                            String str = new String(buffer,0,i);
250
                            output.append(str);                    
251
                    }
252
                                
253
                    is.close();
254
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
255
                                    ||output.toString().toLowerCase().startsWith("<?xml")
256
                                    ||(outputFormat.indexOf("gml") != -1))
257
                    {
258
                            int tag;
259
                            KXmlParser kxmlParser = null;
260
                            kxmlParser = new KXmlParser();            
261
                            kxmlParser.setInput(new StringReader(output.toString()));
262
                            
263
                            tag = kxmlParser.nextTag();           
264
                            if (kxmlParser.getName().compareTo(ServiceException)==0)
265
                                {
266
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
267
                                    return sb.toString();                                                                        
268
                                }
269
                                else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
270
                                {
271
                                        return output.toString();
272
                                }
273
                                else                
274
                                {
275
                                        return output.toString();                
276
                                }
277
                    }
278
                    else
279
                    {
280
                            sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
281
                            return sb.toString();
282
                    }
283
                }
284
            catch(XmlPullParserException parserEx)
285
            {
286
                    if (output.toString().toLowerCase().indexOf("xml") != -1)
287
                    {
288
                            return output.toString().trim();
289
                    }
290
                    else
291
                    {
292
                               sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
293
                        return sb.toString();
294
                    }
295
            }
296
            catch(Exception e)
297
            {
298
                    e.printStackTrace();
299
                    sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
300
                    return sb.toString();
301

    
302
            }
303
    }
304
    
305
    public void getMap(WMSStatus status, ICancellable cancel) 
306
    {      
307
                try
308
                {
309
                        mapRequest = buildMapRequest(status, cancel);
310
                        mapRequest.setFileName("wmsGetMap");
311
                        mapRequest = RequestManager.getInstance().addURLRequest(mapRequest, getMapListener);
312
                        
313
            } catch(MalformedURLException e) {
314
                        e.printStackTrace();
315
                }
316
    } 
317
    
318
    
319
    /* (non-Javadoc)
320
     * @see org.gvsig.remoteClient.wms.WMSProtocolHandler#parseException(byte[])
321
     */
322
    protected String parseException(byte[] data) {
323
        ArrayList errors = new ArrayList();
324
        KXmlParser kxmlParser = new KXmlParser();
325
        try
326
        {
327
            kxmlParser.setInput(new ByteArrayInputStream(data), encoding);        
328
            kxmlParser.nextTag();
329
            int tag;
330
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT ) 
331
            { 
332
                kxmlParser.require(KXmlParser.START_TAG, null, ExceptionTags.EXCEPTION_ROOT);             
333
                tag = kxmlParser.nextTag();
334
                 while(tag != KXmlParser.END_DOCUMENT)
335
                 {
336
                     switch(tag)
337
                     {
338
                        case KXmlParser.START_TAG:
339
                            if (kxmlParser.getName().compareTo(ExceptionTags.SERVICE_EXCEPTION)==0){
340
                                String errorCode = kxmlParser.getAttributeValue("", ExceptionTags.CODE);
341
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
342
                                String errorMessage = kxmlParser.nextText();
343
                                errors.add(errorCode+errorMessage);
344
                            }
345
                            break;
346
                        case KXmlParser.END_TAG:                            
347
                            break;
348
                        
349
                     }
350
                     tag = kxmlParser.nextTag();
351
                 }
352
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
353
            }
354
        }
355
        catch(XmlPullParserException parser_ex){ 
356
            parser_ex.printStackTrace();
357
        }
358
        catch (IOException ioe) {           
359
            ioe.printStackTrace();            
360
        }
361
        String message = errors.size()>0? "" : null;
362
        for (int i = 0; i < errors.size(); i++) {
363
            message += (String) errors.get(i)+"\n";
364
        }
365
        return message;
366
    }
367
    /**
368
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
369
     * without a VERSION, to get the highest version than a WMS supports.
370
     */
371
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version)
372
    {
373
                String req = new String();                
374
        String symbol = getSymbol(_host);
375
        req = req + _host + symbol + "REQUEST=GetCapabilities&SERVICE=WMS&";                
376
        if((_version != null) && (_version.length()>0 ))
377
        {
378
                req += ("&VERSION=" + _version);
379
        }
380
                req += ("&EXCEPTIONS=XML");
381
                return req;           
382
    }
383

    
384
    /**
385
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
386
     * @param WMSStatus
387
     * @throws MalformedURLException 
388
     */
389
    private URLRequest buildCapabilitiesRequest(WMSStatus status) throws MalformedURLException
390
    {
391
                URLRequest req = new URLRequest();
392
                StringBuffer u = new StringBuffer();
393
                String onlineResource;
394
                if (status == null || status.getOnlineResource() == null)
395
                        onlineResource = getHost();
396
                else 
397
                        onlineResource = status.getOnlineResource();
398
                u.append(onlineResource).
399
                  append(getSymbol(onlineResource)).
400
                  append("REQUEST=GetCapabilities&SERVICE=WMS&").
401
                  append("VERSION=").append(getVersion()).append("&EXCEPTIONS=XML");
402
                URL url = new URL(u.toString());
403
                req.setHost(url.getHost());
404
                req.setRequestType(URLRequest.GET);
405
                req.setProtocol(URLRequest.HTTP);
406
                req.setFile(url.getFile());
407
                int myPort = url.getPort();
408
                if (myPort != -1 && myPort != 80 )
409
                        req.setPort(myPort);
410
                
411
                return req;
412
    }
413
    
414
    /**
415
     * Builds the GetFeatureInfoRequest according to the OGC WMS Specifications
416
     */
417
    private String buildGetFeatureInfoRequest(WMSStatus status, int x, int y)
418
    {
419
                StringBuffer req = new StringBuffer();
420
                String symbol = null;
421

    
422
                String onlineResource;
423
                if (status.getOnlineResource() == null)
424
                        onlineResource = getHost();
425
                else 
426
                        onlineResource = status.getOnlineResource();
427
                symbol = getSymbol(onlineResource);
428
        
429
                req.append(onlineResource).append(symbol).append("REQUEST=GetFeatureInfo&SERVICE=WMS&");
430
                req.append("QUERY_LAYERS=").append(Utilities.Vector2CS(status.getLayerNames())); 
431
                req.append("&VERSION=").append(getVersion()).append("&INFO_FORMAT=application/vnd.ogc.gml&");
432
                req.append(getPartialQuery(status)).append("&x="+x + "&y="+y);
433
       if (status.getExceptionFormat() != null) {
434
            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
435
        } else {
436
            req.append("&EXCEPTIONS=XML");
437
        }
438
                return req.toString().replaceAll(" ", "%20");
439
    }    
440

    
441
    /**
442
     * Builds the GetMapRequest according to the OGC WMS Specifications
443
     * @throws MalformedURLException 
444
     */
445
    private URLRequest buildMapRequest(WMSStatus status, ICancellable cancel) throws MalformedURLException
446
    { 
447
                URLRequest req = new URLRequest();
448
                StringBuffer u = new StringBuffer();
449
                String onlineResource = null;
450
                
451
                if (status.getOnlineResource() == null)
452
                        onlineResource = getHost();
453
                else 
454
                        onlineResource = status.getOnlineResource();
455
                u.append(onlineResource +  getSymbol(onlineResource) + "REQUEST=GetMap&SERVICE=WMS&VERSION=").append(getVersion()).append("&");
456
                u.append(getPartialQuery(status));
457
        if (status.getExceptionFormat() != null) {
458
            u.append("&EXCEPTIONS=" + status.getExceptionFormat());
459
        } else {
460
            u.append("&EXCEPTIONS=XML");
461
        }
462
        URL url = new URL(u.toString());
463
                
464
        req.setHost(url.getHost());
465
                req.setRequestType(URLRequest.GET);
466
                req.setProtocol(URLRequest.HTTP);
467
                req.setFile(url.getFile());
468
                int myPort = url.getPort();
469
                if (myPort != -1 && myPort != 80 )
470
                        req.setPort(myPort);
471
                req.setCancel(cancel);
472
                return req;
473
    }
474
    
475
    /**
476
     * Just for not repeat code. Gets the correct separator according to the server URL
477
     * @param h
478
     * @return
479
     */
480
    private static String getSymbol(String h) {
481
        String symbol;
482
        if (h.indexOf("?")==-1) 
483
            symbol = "?";
484
        else if (h.indexOf("?")!=h.length()-1)
485
            symbol = "&";
486
        else
487
            symbol = "";
488
        return symbol;
489
    }
490

    
491
    /**
492
     * Gets the part of the OGC request that share GetMap and GetFeatureInfo
493
     * @return String request
494
     */
495
    public String getPartialQuery(WMSStatus status)
496
    {            
497
        StringBuffer req = new StringBuffer();
498
        req.append("LAYERS=" + Utilities.Vector2CS(status.getLayerNames()))
499
           .append("&SRS=" + status.getSrs())
500
           .append("&BBOX=" + status.getExtent().getMinX()+ "," )
501
           .append(status.getExtent().getMinY()+ ",")
502
           .append(status.getExtent().getMaxX()+ ",")
503
           .append(status.getExtent().getMaxY())
504
           .append("&WIDTH=" + status.getWidth())
505
           .append("&HEIGHT=" + status.getHeight())
506
           .append("&FORMAT=" + status.getFormat())
507
           .append("&STYLES=");
508
        Vector v = status.getStyles();
509
        if (v!=null && v.size()>0)
510
                req.append(Utilities.Vector2CS(v)); 
511
        v = status.getDimensions();
512
        if (v!=null && v.size()>0)
513
            req.append("&" + Utilities.Vector2URLParamString(v));
514
        if (status.getTransparency()) {
515
            req.append("&TRANSPARENT=TRUE");
516
        }
517
        return req.toString();
518
    }
519

    
520
    
521
    
522
    public void close() {        
523
        // your code here
524
    } 
525
    
526
    /**
527
     * Inner class that represents the description of the WMS metadata.
528
     * The first part of the capabilities will return the service information
529
     * from the WMS, this class will hold this information. 
530
     * 
531
     */
532
    public class ServiceInformation {
533

    
534
        public String online_resource = null;
535
        /*public String map_online_resource = null;
536
        public String feature_online_resource = null;*/
537
        public String version;
538
        public String name;
539
        public String scope;
540
        public String title;
541
        public String abstr;
542
        public String keywords;
543
        public String fees;
544
        public String operationsInfo;
545
        public String personname;
546
        public String organization;
547
        public String function;
548
        public String addresstype;
549
        public String address;
550
        public String place;
551
        public String province;
552
        public String postcode;
553
        public String country;
554
        public String phone;
555
        public String fax;
556
        public String email;
557
        public Vector formats;
558
        public HashMap operations; // operations that WMS supports
559
        
560
        public ServiceInformation()
561
        {          
562
            version = new String();
563
            name = new String();
564
            scope = new String();
565
            title = new String();
566
            abstr = new String();
567
            keywords = new String();
568
            fees = new String();
569
            operationsInfo = new String();
570
            personname = new String();
571
            organization = new String();
572
            function = new String();
573
            addresstype = new String();
574
            address = new String();
575
            place = new String();
576
            province = new String();
577
            postcode = new String();
578
            country = new String();
579
            phone = new String();
580
            fax = new String();
581
            email = new String();
582
            formats = new Vector();               
583
            operations = new HashMap();            
584
        }
585
        public boolean isQueryable()
586
        {
587
                if (operations.keySet().contains( CapabilitiesTags.GETFEATUREINFO ))
588
                        return true;
589
                else
590
                        return false;
591
        }
592
     }
593

    
594
        public void setListener(WMSEventListener listener) {
595
                if (listener!=null)
596
                        this.theListener = listener;
597
        }
598

    
599
        public void cancel(int opCode) {
600
                URLRequest myUrlReq;
601
                RetrieveListener myListener;
602
                switch (opCode) {
603
                case WMSClient.GET_CAPABILITIES:
604
                        myUrlReq = capabilitiesRequest;
605
                        myListener = getCapabilitiesListener;
606
                        break;
607
                case WMSClient.GET_MAP:
608
                        myUrlReq = mapRequest;
609
                        myListener = getMapListener;
610
                        break;
611
                case WMSClient.GET_FEATURE_INFO:
612
                        myUrlReq = featureInfo;
613
                        myListener = getFeatureInfoListener;
614
                        break;
615
                default:
616
                        System.err.println("[RemoteClients:WMSProtocolHandler] cancelling an unknown operation");
617
                        return;
618
                }
619
                myUrlReq.removeRetrieveListener(myListener);
620
        }   
621
 }