Statistics
| Revision:

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

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

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

    
33
import com.iver.andami.PluginServices;
34

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

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

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

    
101
                public void transferEventReceived(RetrieveEvent event) {
102
                        int type = event.getType();
103
                        
104
                }
105
            
106
    } ,
107
    getFeatureInfoListener = new RetrieveListener() {
108
                public void transferEventReceived(RetrieveEvent event) {
109
                        int type = event.getType();
110
                        
111
                }};
112
    /**
113
     * parses the data retrieved by the WMS in XML format. 
114
     * It will be mostly the WMS Capabilities, but the implementation
115
     * will be placed in the handler implementing certain version of the protocol.
116
     * 
117
     */
118
    public abstract void parse(File f) ;
119

    
120
    public String getName() {        
121
            return name;
122
    } 
123

    
124
    public String getVersion() {        
125
            return version;
126
    }    
127
    
128
    public ServiceInformation getServiceInformation() {        
129
        return serviceInfo;
130
    }  
131
    public String getHost ()
132
    {
133
            return host;
134
    }
135
    public void setHost(String _host)
136
    {
137
            host = _host;
138
    }
139
    public String getPort()
140
    {
141
            return port;
142
    }
143
    public void setPort(String _port)
144
    {
145
            port = _port;
146
    }
147
    
148

    
149
    /**
150
         * <p>Builds a GetCapabilities request that is sent to the WMS
151
         * the response will be parsed to extract the data needed by the
152
         * WMS client</p>
153
         * @param override, if true the previous downloaded data will be overridden
154
         */
155
    public void getCapabilities(WMSStatus status, boolean override)
156
    {                
157
            
158
                try
159
                {
160
                        //request = new URL(buildCapabilitiesRequest(status));
161
                        capabilitiesRequest = buildCapabilitiesRequest(status);
162
                        capabilitiesRequest.setFileName("wmsCapabilities");
163
                        if (override)
164
                                RequestManager.getInstance().removeURLRequest(capabilitiesRequest);
165
                        capabilitiesRequest = RequestManager.getInstance().addURLRequest(capabilitiesRequest, getCapabilitiesListener);
166
                        
167
            } catch(Exception e)
168
                {
169
                        //TODO
170
                        e.printStackTrace();
171
                }
172
    }
173

    
174
    /**
175
     * <p>It will send a GetFeatureInfo request to the WMS
176
     * Parsing the response and redirecting the info to the WMS client</p>
177
     */
178
    public String getFeatureInfo(WMSStatus status, int x, int y, int featureCount)
179
    {
180
            URL request = null;
181
            StringBuffer output = new StringBuffer();
182
            String outputFormat = new String();
183
            String ServiceException = "ServiceExceptionReport";                        
184
            StringBuffer sb = new StringBuffer();
185
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
186
                try
187
                {
188
                        //TODO:
189
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
190
                        request = new URL(buildGetFeatureInfoRequest(status, x, y));
191
                        
192
                        //TODO:
193
                        //see which output format is being requested.                                         
194
                }
195
                catch(Exception e)
196
                {
197
                        e.printStackTrace();
198
                        return "";
199
                }
200
                
201
            try
202
            {                                
203
                    System.out.println(request.toString());          
204
                    byte[] buffer = new byte[1024*256];                    
205
                    DataInputStream is = new DataInputStream(request.openStream());                    
206
                    outputFormat = request.openConnection().getContentType();
207

    
208
                    for (int i = is.read(buffer); i>0; i = is.read(buffer))
209
                    {
210
                            String str = new String(buffer,0,i);
211
                            output.append(str);                    
212
                    }
213
                                
214
                    is.close();
215
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
216
                                    ||output.toString().toLowerCase().startsWith("<?xml")
217
                                    ||(outputFormat.indexOf("gml") != -1))
218
                    {
219
                            int tag;
220
                            KXmlParser kxmlParser = null;
221
                            kxmlParser = new KXmlParser();            
222
                            kxmlParser.setInput(new StringReader(output.toString()));
223
                            
224
                            tag = kxmlParser.nextTag();           
225
                            if (kxmlParser.getName().compareTo(ServiceException)==0)
226
                                {
227
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
228
                                    return sb.toString();                                                                        
229
                                }
230
                                else if (kxmlParser.getName().compareToIgnoreCase("ERROR")==0)
231
                                {
232
                                        return output.toString();
233
                                }
234
                                else                
235
                                {
236
                                        return output.toString();                
237
                                }
238
                    }
239
                    else
240
                    {
241
                            sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
242
                            return sb.toString();
243
                    }
244
                }
245
            catch(XmlPullParserException parserEx)
246
            {
247
                    if (output.toString().toLowerCase().indexOf("xml") != -1)
248
                    {
249
                            return output.toString().trim();
250
                    }
251
                    else
252
                    {
253
                               sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
254
                        return sb.toString();
255
                    }
256
            }
257
            catch(Exception e)
258
            {
259
                    e.printStackTrace();
260
                    sb.append("<INFO>").append(PluginServices.getText(this, "info_format_not_supported")).append("</INFO>");
261
                    return sb.toString();
262

    
263
            }
264
    }
265
    
266
    public File getMap(WMSStatus status) throws ServerErrorException, WMSException
267
    {        
268
            URL request = null;
269
                try
270
                {
271
                        //TODO:
272
                        //pass this buildXXXRequest to the WMSProtocolHandlerXXX: The request can depend on the WMS version.
273
                        request = new URL(buildMapRequest(status));
274
            
275
            File f = com.iver.andami.Utilities.downloadFile(request, "wmsGetMap");                                        
276
                    
277
            if (Utilities.isTextFile(f)) {
278
                            FileInputStream fis = new FileInputStream(f);
279
                            FileChannel fc = fis.getChannel();
280
                            byte[] data = new byte[(int)fc.size()];   // fc.size returns the size of the file which backs the channel
281
                            ByteBuffer bb = ByteBuffer.wrap(data);
282
                            fc.read(bb);
283
                                                        
284
                            WMSException wmsEx = null;
285
                       
286
                    String exceptionMessage = parseException(data);
287
                if (exceptionMessage==null)
288
                {
289
                         String error = new String(data);
290
                        int pos = error.indexOf("<?xml");
291
                        if (pos!= -1)
292
                        {
293
                                String xml = error.substring(pos,error.length());
294
                                exceptionMessage = parseException(xml.getBytes());
295
                        }               
296
                    if (exceptionMessage == null)
297
                            exceptionMessage = new String(data);
298
                        
299
                }
300
                     wmsEx = new WMSException(exceptionMessage);
301
                    wmsEx.setWMSMessage(new String(data));
302
                    
303
                    // Since it is an error file, It must be deleted from the cache
304
                    com.iver.andami.Utilities.removeURL(request);
305
                throw wmsEx;
306
            }
307
                        return f;                    
308
                }
309
                catch(IOException e)
310
                {
311
                        e.printStackTrace();
312
            throw new ServerErrorException();
313
                }
314
    } 
315
    
316
    
317
    /* (non-Javadoc)
318
     * @see org.gvsig.remoteClient.wms.WMSProtocolHandler#parseException(byte[])
319
     */
320
    protected String parseException(byte[] data) {
321
        ArrayList errors = new ArrayList();
322
        KXmlParser kxmlParser = new KXmlParser();
323
        try
324
        {
325
            kxmlParser.setInput(new ByteArrayInputStream(data), encoding);        
326
            kxmlParser.nextTag();
327
            int tag;
328
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT ) 
329
            { 
330
                kxmlParser.require(KXmlParser.START_TAG, null, ExceptionTags.EXCEPTION_ROOT);             
331
                tag = kxmlParser.nextTag();
332
                 while(tag != KXmlParser.END_DOCUMENT)
333
                 {
334
                     switch(tag)
335
                     {
336
                        case KXmlParser.START_TAG:
337
                            if (kxmlParser.getName().compareTo(ExceptionTags.SERVICE_EXCEPTION)==0){
338
                                String errorCode = kxmlParser.getAttributeValue("", ExceptionTags.CODE);
339
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
340
                                String errorMessage = kxmlParser.nextText();
341
                                errors.add(errorCode+errorMessage);
342
                            }
343
                            break;
344
                        case KXmlParser.END_TAG:                            
345
                            break;
346
                        
347
                     }
348
                     tag = kxmlParser.nextTag();
349
                 }
350
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
351
            }
352
        }
353
        catch(XmlPullParserException parser_ex){ 
354
            parser_ex.printStackTrace();
355
        }
356
        catch (IOException ioe) {           
357
            ioe.printStackTrace();            
358
        }
359
        String message = errors.size()>0? "" : null;
360
        for (int i = 0; i < errors.size(); i++) {
361
            message += (String) errors.get(i)+"\n";
362
        }
363
        return message;
364
    }
365
    /**
366
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
367
     * without a VERSION, to get the highest version than a WMS supports.
368
     */
369
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version)
370
    {
371
                String req = new String();                
372
        String symbol = getSymbol(_host);
373
        req = req + _host + symbol + "REQUEST=GetCapabilities&SERVICE=WMS&";                
374
        if((_version != null) && (_version.length()>0 ))
375
        {
376
                req += ("&VERSION=" + _version);
377
        }
378
                req += ("&EXCEPTIONS=XML");
379
                return req;           
380
    }
381

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

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

    
440
    /**
441
     * Builds the GetMapRequest according to the OGC WMS Specifications
442
     */
443
    private String buildMapRequest(WMSStatus status)
444
    { 
445
                StringBuffer req = new StringBuffer();
446
                String symbol = null;
447
                String onlineResource = null;
448
                
449
                if (status.getOnlineResource() == null)
450
                        onlineResource = getHost();
451
                else 
452
                        onlineResource = status.getOnlineResource();
453
                symbol = getSymbol(onlineResource);
454
        
455
                req.append(onlineResource + symbol + "REQUEST=GetMap&SERVICE=WMS&VERSION=").append(getVersion()).append("&");
456
                req.append(getPartialQuery(status));
457
        if (status.getExceptionFormat() != null) {
458
            req.append("&EXCEPTIONS=" + status.getExceptionFormat());
459
        } else {
460
            req.append("&EXCEPTIONS=XML");
461
        }
462
                return req.toString().replaceAll(" ", "%20");
463
    }
464
    
465
    /**
466
     * Just for not repeat code. Gets the correct separator according to the server URL
467
     * @param h
468
     * @return
469
     */
470
    private static String getSymbol(String h) {
471
        String symbol;
472
        if (h.indexOf("?")==-1) 
473
            symbol = "?";
474
        else if (h.indexOf("?")!=h.length()-1)
475
            symbol = "&";
476
        else
477
            symbol = "";
478
        return symbol;
479
    }
480

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

    
510
    
511
    
512
    public void close() {        
513
        // your code here
514
    } 
515
    
516
    /**
517
     * Inner class that represents the description of the WMS metadata.
518
     * The first part of the capabilities will return the service information
519
     * from the WMS, this class will hold this information. 
520
     * 
521
     */
522
    public class ServiceInformation {
523

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

    
584
        public void setListener(WMSEventListener listener) {
585
                if (listener!=null)
586
                        this.theListener = listener;
587
        }
588

    
589
        public void cancel(int opCode) {
590
                URLRequest myUrlReq;
591
                RetrieveListener myListener;
592
                switch (opCode) {
593
                case WMSClient.GET_CAPABILITIES:
594
                        myUrlReq = capabilitiesRequest;
595
                        myListener = getCapabilitiesListener;
596
                        break;
597
                case WMSClient.GET_MAP:
598
                        myUrlReq = mapRequest;
599
                        myListener = getMapListener;
600
                        break;
601
                case WMSClient.GET_FEATURE_INFO:
602
                        myUrlReq = featureInfo;
603
                        myListener = getFeatureInfoListener;
604
                        break;
605
                default:
606
                        System.err.println("[RemoteClients:WMSProtocolHandler] cancelling an unknown operation");
607
                        return;
608
                }
609
                myUrlReq.removeRetrieveListener(myListener);
610
        }   
611
 }