Statistics
| Revision:

gvsig-raster / org.gvsig.raster.wmts / trunk / org.gvsig.raster.wmts / org.gvsig.raster.wmts.ogc / org.gvsig.raster.wmts.ogc.impl / src / main / java / org / gvsig / raster / wmts / ogc / impl / base / WMTSProtocolHandler.java @ 1961

History | View | Annotate | Download (20.8 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
*
3
* Copyright (C) 2007-2008 Infrastructures and Transports Department
4
* of the Valencian Government (CIT)
5
*
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
10
*
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
* GNU General Public License for more details.
15
*
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
* MA  02110-1301, USA.
20
*
21
*/
22
package org.gvsig.raster.wmts.ogc.impl.base;
23

    
24
import java.io.BufferedOutputStream;
25
import java.io.ByteArrayInputStream;
26
import java.io.DataInputStream;
27
import java.io.DataOutputStream;
28
import java.io.File;
29
import java.io.FileInputStream;
30
import java.io.FileOutputStream;
31
import java.io.FileReader;
32
import java.io.IOException;
33
import java.net.HttpURLConnection;
34
import java.net.MalformedURLException;
35
import java.net.URL;
36
import java.security.KeyManagementException;
37
import java.security.NoSuchAlgorithmException;
38
import java.util.ArrayList;
39
import java.util.Hashtable;
40
import java.util.List;
41
import java.util.StringTokenizer;
42
import java.util.prefs.Preferences;
43

    
44
import javax.net.ssl.HttpsURLConnection;
45
import javax.net.ssl.SSLContext;
46
import javax.net.ssl.TrustManager;
47
import javax.net.ssl.X509TrustManager;
48

    
49
import org.gvsig.compat.CompatLocator;
50
import org.gvsig.compat.net.Downloader;
51
import org.gvsig.compat.net.ICancellable;
52
import org.gvsig.raster.wmts.ogc.WMTSStatus;
53
import org.gvsig.raster.wmts.ogc.exception.DownloadException;
54
import org.gvsig.raster.wmts.ogc.exception.ServerErrorException;
55
import org.gvsig.raster.wmts.ogc.exception.WMTSException;
56
import org.gvsig.raster.wmts.ogc.impl.Tags;
57
import org.gvsig.raster.wmts.ogc.impl.Utilities;
58
import org.gvsig.raster.wmts.ogc.impl.request.WMTSGetCapabilitiesRequest;
59
import org.gvsig.raster.wmts.ogc.impl.request.WMTSGetFeatureInfoRequest;
60
import org.gvsig.raster.wmts.ogc.impl.request.WMTSGetTileRequest;
61
import org.gvsig.raster.wmts.ogc.struct.WMTSOperationsMetadata;
62
import org.kxml2.io.KXmlParser;
63
import org.xmlpull.v1.XmlPullParserException;
64

    
65
/**
66
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.</p>
67
 * 
68
 * @author Nacho Brodin (nachobrodin@gmail.com)
69
 */
70
public abstract class WMTSProtocolHandler {
71
        protected static final Downloader downloader = CompatLocator.getDownloader();
72
        /**
73
         * Encoding used to parse different xml documents.
74
         */
75
        protected String                 encoding                     = "UTF-8";
76
        private static int               count                        = 0;
77
        final String                     tempDirectoryPath            = System.getProperty("java.io.tmpdir")+"/tmp-andami";
78
        protected boolean                forceLongitudeFirstAxisOrder = false;
79
    protected String                 name;
80
    protected String                 version;
81
    protected String                 host;
82
    protected String                 port;  
83
    private WMTSServerDescription    serverDescription            = null;
84
        
85
    protected abstract WMTSGetFeatureInfoRequest createGetFeatureInfoRequest(WMTSStatusImpl status, int x, int y);
86

    
87
    protected abstract WMTSGetTileRequest createGetTileRequest(WMTSStatusImpl status);
88
    
89
    protected abstract WMTSGetCapabilitiesRequest createGetCapabilitiesRequest();
90
    
91
    /**
92
     * parses the data retrieved by the Capabilities XML document
93
     */
94
    public abstract boolean parseCapabilities(File f);
95
    
96
        public WMTSServerDescription getServerDescription() {
97
                if(serverDescription == null) {
98
                        serverDescription = new WMTSServerDescription(getVersion());
99
                }
100
                return this.serverDescription;
101
        }
102

    
103
    /**
104
     * @return Returns the host.
105
     */
106
    public String getHost() {
107
        return host;
108
    }
109
    /**
110
     * @param host The host to set.
111
     */
112
    public void setHost(String host) {
113
        this.host = host;
114
    }
115
    /**
116
     * @return Returns the name.
117
     */
118
    public String getName() {
119
        return name;
120
    }
121
    /**
122
     * @param name The name to set.
123
     */
124
    public void setName(String name) {
125
        this.name = name;
126
    }
127
    /**
128
     * @return Returns the port.
129
     */
130
    public String getPort() {
131
        return port;
132
    }
133
    /**
134
     * @param port The port to set.
135
     */
136
    public void setPort(String port) {
137
        this.port = port;
138
    }
139
    /**
140
     * @return Returns the version.
141
     */
142
    public String getVersion() {
143
        return version;
144
    }
145
    /**
146
     * @param version The version to set.
147
     */
148
    public void setVersion(String version) {
149
        this.version = version;
150
    }
151

    
152
    /**
153
     * Just for not repeat code. Gets the correct separator according 
154
     * to the server URL
155
     * @param h
156
     * @return
157
     */
158
    public String getSymbol(String h) {
159
        String symbol;
160
        if (h.indexOf("?")==-1) 
161
            symbol = "?";
162
        else if (h.indexOf("?")!=h.length()-1)
163
            symbol = "&";
164
        else
165
            symbol = "";
166
        return symbol;
167
    }  
168

    
169
    /**
170
     * Copy the file in a byte array
171
     * @param file
172
     * The file to copy
173
     * @return
174
     * An array of bytes
175
     * @throws IOException
176
     */
177
    protected byte[] fileToBytes(File file) throws IOException {
178
        FileInputStream fis = null;
179
        byte[] bytes = null;
180
        try{
181
            fis = new FileInputStream(file);
182

    
183
            //long length = file.length(); 
184
            bytes = new byte[(int)file.length()];
185

    
186
            int offset = 0;
187
            int numRead = 0; 
188
            while (offset < bytes.length && 
189
                    (numRead = fis.read(bytes, offset, bytes.length-offset)) >= 0) { 
190
                offset += numRead; 
191
            }
192
        } catch (IOException e) {
193
            throw e;
194
        } finally {
195
            if (fis != null) {
196
                fis.close();
197
            }
198
        }         
199
        return bytes;
200
    }
201
    
202
    /**
203
         * Sets longitude first in the axis order read from the capabilities file
204
         * @param force
205
         */
206
        public void setForceLongitudeFirstAxisOrder(boolean force) {
207
                this.forceLongitudeFirstAxisOrder = force;
208
        }
209
        
210
    //**************************************************************
211
    //Esto es temporal hasta que est? la cach?
212
    
213
        private Hashtable<URL, String>                downloadedFiles;
214
    /**
215
         * Returns the content of a URL as a file from the file system.<br>
216
         * <p>
217
         * If the URL has been already downloaded in this session and notified
218
         * to the system using the static <b>Utilities.addDownloadedURL(URL)</b>
219
         * method, it can be restored faster from the file system avoiding to
220
         * download it again.
221
         * </p>
222
         * @param url
223
         * @return File containing this URL's content or null if no file was found.
224
         */
225
        private File getPreviousDownloaded(Object object) {
226
                File f = null;
227
                if (downloadedFiles != null && downloadedFiles.containsKey(object)) {
228
                        String filePath = (String) downloadedFiles.get(object);
229
                        f = new File(filePath);
230
                        if (!f.exists())
231
                                return null;
232
                }
233
                return f;
234
        }
235

    
236
        /**
237
         * Adds an URL to the table of downloaded files for further uses. If the URL
238
         * already exists in the table its filePath value is updated to the new one and
239
         * the old file itself is removed from the file system.
240
         *
241
         * @param url
242
         * @param filePath
243
         */
244
        void addDownloadedURL(URL url, String filePath) {
245
                if (downloadedFiles == null)
246
                        downloadedFiles = new Hashtable<URL, String>();
247
                downloadedFiles.put(url, filePath);
248
        }
249
        
250
        //Fin del c?digo temporal hasta que est? la cach?
251
        //**************************************************************
252
    
253
    public synchronized File getTile(WMTSStatusImpl status, ICancellable cancel, File file) throws ServerErrorException, WMTSException {
254
            try {
255
                        WMTSGetTileRequest request = createGetTileRequest(status);
256
                        request.sendRequest(cancel, file);
257
                        try {
258
                                checkFileError(file);
259
                        } catch(WMTSException e) {
260
                                file.delete();
261
                                throw new WMTSException("This tile is not available:" + file.getAbsoluteFile().toString(), e);
262
                        } catch(IOException e) {
263
                                file.delete();
264
                                throw new WMTSException("This tile is not available:" + file.getAbsoluteFile().toString(), e);
265
                        }
266
                        return file;
267
                } catch(IOException e) {
268
            throw new WMTSException(e);
269
                }
270
    }
271
    
272
    public synchronized File getTile(WMTSStatus status, ICancellable cancel) throws ServerErrorException, WMTSException {
273
            try {
274
                        WMTSGetTileRequest request = createGetTileRequest((WMTSStatusImpl)status);
275
                        File f = request.sendRequest(cancel);
276
                        try {
277
                                checkFileError(f);
278
                        } catch(WMTSException e) {
279
                                downloader.removeURL(request);
280
                    throw new ServerErrorException();
281
                        }
282
                        return f;
283
                } catch(IOException e) {
284
                        e.printStackTrace();
285
            throw new ServerErrorException();
286
                }
287
    }
288
    
289
    /**
290
     * Checks if the file downloaded is correct or doesn't
291
     * @param f
292
     * @throws WMTSException
293
     * @throws IOException
294
     */
295
    private void checkFileError(File f) throws WMTSException, IOException {
296
                if (f == null)
297
                    return;
298
            if (Utilities.isTextFile(f)) {
299
            byte[] data = fileToBytes(f);
300

    
301
                String exceptionMessage = parseException(data);
302
            if (exceptionMessage == null) {
303
                     String error = new String(data);
304
                    int pos = error.indexOf("<?xml");
305
                    if (pos!= -1) {
306
                            String xml = error.substring(pos,error.length());
307
                            exceptionMessage = parseException(xml.getBytes());
308
                    }
309
                if (exceptionMessage == null)
310
                        exceptionMessage = new String(data);
311

    
312
            }
313
            throw new WMTSException(exceptionMessage);
314
        }
315
    }
316
    
317
    public synchronized URL getTileURL(WMTSStatusImpl status) throws MalformedURLException {
318
            String onlineResource = getBaseURL(Tags.GETTILE);
319

    
320
                StringBuffer req = new StringBuffer();
321
                req.append(onlineResource)
322
                .append("REQUEST=GetTile&SERVICE=WMTS&VERSION=").append(getVersion()).append("&")
323
                .append("Layer=" + status.getLayer())
324
        .append("&Style=" + status.getStyle())
325
        .append("&Format=" + status.getFormat())
326
        .append("&TileMatrixSet=" + status.getTileMatrixSet())
327
        .append("&TileMatrix=" + status.getTileMatrix())
328
        .append("&TileRow=" + status. getTileRow())
329
        .append("&TileCol=" + status.getTileCol());
330
                
331
                return new URL(req.toString());
332
    }
333
    
334
    /**
335
     * Gets the URL base for a operation (GetCapabilities, GetTile, ...)
336
     * @param operation
337
     * @return the base URL for the GET protocol or the host passed by the user 
338
     * if it does not exists. 
339
     */
340
    public String getBaseURL(String operation) {
341
            String onlineResource = null;
342
            WMTSServerDescription svDescription = getServerDescription();
343
            if(svDescription != null) {
344
                    WMTSOperationsMetadata opMetadata = svDescription.getOperationsMetadata();
345
                    if(opMetadata != null)
346
                            onlineResource = opMetadata.getOnlineResource(operation);
347
            }
348
            if(onlineResource == null)
349
                    onlineResource = getHost();
350
                String symbol = getSymbol(onlineResource);
351
                onlineResource = onlineResource + symbol;
352
                return onlineResource;
353
    }
354
    
355
    /**
356
     * Gets the URL base for a operation (GetCapabilities, GetTile, ...) and 
357
     * a protocol GET or POST
358
     * @param operation
359
     * @return the base URL for this protocol or null if it does not exists. 
360
     */
361
    public String getBaseURL(String operation, int protocol) {
362
            String onlineResource = null;
363
            WMTSServerDescription svDescription = getServerDescription();
364
            if(svDescription != null) {
365
                    WMTSOperationsMetadata opMetadata = svDescription.getOperationsMetadata();
366
                    if(opMetadata != null) {
367
                            onlineResource = opMetadata.getOnlineResource(operation, protocol);
368
                            if(onlineResource != null) {
369
                                    String symbol = getSymbol(onlineResource);
370
                                    return onlineResource + symbol;
371
                            }
372
                    }
373
            }
374
                return null;
375
    }
376
    
377
    /**
378
     * Gets a file name to download. I
379
     * @param url
380
     * @return
381
     */
382
    private File getFile() {
383
            count ++;
384
                int index = name.lastIndexOf(".");
385
                if (index > 0){
386
                        return new File(tempDirectoryPath + "/" + name.substring(0,index) + System.currentTimeMillis() + count + 
387
                                name.substring(index, name.length()));
388
                }
389
                return new File(tempDirectoryPath + "/" + name + System.currentTimeMillis() + count);
390
    }
391
    
392
    public synchronized File downloadFile(URL url, ICancellable cancel) throws DownloadException {
393
            File f = null;
394
            if((f = getPreviousDownloaded(url)) != null)
395
                    return f;
396
                    
397
            Preferences prefs = Preferences.userRoot().node( "gvsig.downloader" );
398
            int IO_BUFFER_SIZE = 8 * 1024;
399
                int timeout = prefs.getInt("timeout", 60000);
400
                
401
                File dstFile = getFile();
402
                
403
                DataOutputStream dos;
404
                try {
405
                        DataInputStream is;
406
                        //OutputStreamWriter os = null;
407
                        HttpURLConnection connection = null;
408

    
409
                        if (url.getProtocol().equals("https")) {
410
                                disableHttsValidation();
411
                        }
412
                        connection = (HttpURLConnection)url.openConnection();
413
                        connection.setConnectTimeout(timeout);
414
                        
415
                        is = new DataInputStream(url.openStream());
416
                        
417
                        dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(dstFile)));
418
                        byte[] buffer = new byte[IO_BUFFER_SIZE];
419

    
420
                        int read;
421
                        while ((read = is.read(buffer)) != -1) {
422
                                dos.write(buffer, 0, read);
423
                        }
424
                        
425
                        /*if(os != null) {
426
                                os.close();
427
                        }*/
428
                        dos.close();
429
                        is.close();
430
                        is = null;
431
                        dos = null;
432
                        if (cancel != null && cancel.isCanceled()) {
433
                                dstFile.delete();
434
                                dstFile = null;
435
                                return null;
436
                        }
437
                } catch (Exception e) {
438
                        throw new DownloadException(e);
439
                }
440
                addDownloadedURL(url, dstFile.getAbsolutePath());
441
                return dstFile;
442
    }
443
    
444
    /**
445
         * This method disables the Https certificate validation.
446
         * @throws KeyManagementException
447
         * @throws NoSuchAlgorithmException
448
         */
449
        private void disableHttsValidation() throws KeyManagementException, NoSuchAlgorithmException{
450
                // Create a trust manager that does not validate certificate chains
451
                TrustManager[] trustAllCerts = new TrustManager[] {
452
                                new X509TrustManager() {
453
                                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
454
                                                return null;
455
                                        }
456
                                        public void checkClientTrusted(
457
                                                        java.security.cert.X509Certificate[] certs, String authType) {
458
                                        }
459
                                        public void checkServerTrusted(
460
                                                        java.security.cert.X509Certificate[] certs, String authType) {
461
                                        }
462
                                }
463
                };
464

    
465
                // Install the all-trusting trust manager
466
                SSLContext sc = SSLContext.getInstance("SSL");
467
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
468
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
469
        }
470
    
471
        /**
472
     * <p>It will send a GetFeatureInfo request to the WMTS
473
     * Parsing the response and redirecting the info to the WMTS client</p>
474
     * TODO: return a stored file instead a String.
475
     */
476
    public String getFeatureInfo(WMTSStatusImpl status, int x, int y, ICancellable cancel) {
477
            StringBuffer output = new StringBuffer();
478
            String outputFormat = new String();
479
            String ServiceException = "ServiceExceptionReport";
480
            StringBuffer sb = new StringBuffer();
481
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
482
                try {
483
                        WMTSGetFeatureInfoRequest request = createGetFeatureInfoRequest(status, x, y);
484
                        URL url = request.getURL();
485
                    outputFormat = url.openConnection().getContentType();
486
                    File f = request.sendRequest(cancel);
487
                        if (f == null) {
488
                                return "";
489
                        }
490

    
491
                        FileReader fReader = new FileReader(f);
492
                        char[] buffer = new char[1024*256];
493
                        for (int i = fReader.read(buffer);i > 0;i = fReader.read(buffer)) {
494
                            String str = new String(buffer, 0, i);
495
                            output.append(str);
496
                    }
497
                        fReader.close();
498
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
499
                                    ||output.toString().toLowerCase().startsWith("<?xml")
500
                                    ||(outputFormat.indexOf("gml") != -1)) {
501
                            KXmlParser kxmlParser = null;
502
                            kxmlParser = new KXmlParser();
503
                            //kxmlParser.setInput(new StringReader(output.toString()));
504
                            kxmlParser.setInput(new FileReader(f));
505

    
506
                            kxmlParser.nextTag();
507
                            if (kxmlParser.getName().compareTo(ServiceException) == 0) {
508
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
509
                                    return sb.toString();
510
                                } else if (kxmlParser.getName().compareToIgnoreCase("ERROR") == 0) {
511
                                        return output.toString();
512
                                } else {
513
                                        return output.toString();
514
                                }
515
                    } else {                  
516
                             return output.toString();
517
                    }
518
                } catch(XmlPullParserException parserEx) {
519
                    if (output.toString().toLowerCase().indexOf("xml") != -1) {
520
                            return output.toString().trim();
521
                    } else {
522
                               sb.append("<INFO>").append("Info format not supported").append("</INFO>");
523
                        return sb.toString();
524
                    }
525
            } catch(Exception e) {
526
                    e.printStackTrace();
527
                    sb.append("<INFO>").append("Info format not supported").append("</INFO>");
528
                    return sb.toString();
529
            }
530
    }
531

    
532
    /**
533
         * <p>Builds a GetCapabilities request that is sent to the WMS
534
         * the response will be parse to extract the data needed by the
535
         * WMS client</p>
536
         * @param override, if true the previous downloaded data will be overridden
537
         */
538
    public void getCapabilities(WMTSServerDescription status, boolean override, ICancellable cancel) {
539
            try {
540
                    serverDescription = status;
541
                        WMTSGetCapabilitiesRequest request = createGetCapabilitiesRequest();
542
                        File f = request.sendRequest(cancel);                        
543
        
544
                        if (f == null)
545
                                return;
546
                        parseCapabilities(f);
547
            } catch(Exception e) {
548
                        e.printStackTrace();
549
                }
550
    }
551
    
552
    
553
    /**
554
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
555
     * without a VERSION, to get the highest version than a WMS supports.
556
     */
557
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version) {
558
                int index = _host.indexOf('?');
559
                
560
                if (index > -1) {
561
                        String host = _host.substring(0, index + 1);
562
                        String query = _host.substring(index + 1, _host.length());
563
                        
564
                        StringTokenizer tokens = new StringTokenizer(query, "&");
565
                        String newQuery = "", token;
566

    
567
                        // If there is a field or a value with spaces, (and then it's on different tokens) -> unify them
568
                        while (tokens.hasMoreTokens()) {
569
                                token = tokens.nextToken().trim();
570

    
571
                                if (token.toUpperCase().compareTo("REQUEST=GETCAPABILITIES") == 0)
572
                                        continue;
573

    
574
                                if (token.toUpperCase().compareTo("SERVICE=WMTS") == 0)
575
                                        continue;
576

    
577
                                if ((_version != null) && (_version.length() > 0)) {
578
                                    if (token.toUpperCase().compareTo("VERSION=" + _version) == 0)
579
                                            continue;
580
                                }
581

    
582
                                if (token.toUpperCase().compareTo("EXCEPTIONS=XML") == 0)
583
                                        continue;
584

    
585
                                newQuery += token + "&";
586
                        }
587

    
588
                _host = host + newQuery;
589
                }
590
                else {
591
                        _host += "?";
592
                }
593

    
594
            if ((_version != null) && (_version.compareTo("") != 0))
595
                    _host += "REQUEST=GetCapabilities&SERVICE=WMTS&VERSION=" + _version;
596
            else
597
                    _host += "REQUEST=GetCapabilities&SERVICE=WMTS";
598

    
599
            return _host;
600
    }
601
    
602
        protected String parseException(byte[] data) throws IOException {
603
        List<String> errors = new ArrayList<String>();
604
        KXmlParser kxmlParser = new KXmlParser();
605
        try
606
        {
607
            kxmlParser.setInput(new ByteArrayInputStream(data), encoding);
608
            kxmlParser.nextTag();
609
            int tag;
610
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT )
611
            {
612
                kxmlParser.require(KXmlParser.START_TAG, null, Tags.EXCEPTION_ROOT);
613
                tag = kxmlParser.nextTag();
614
                 while(tag != KXmlParser.END_DOCUMENT)
615
                 {
616
                     switch(tag)
617
                     {
618
                        case KXmlParser.START_TAG:
619
                            if (kxmlParser.getName().compareTo(Tags.SERVICE_EXCEPTION)==0){
620
                                String errorCode = kxmlParser.getAttributeValue("", Tags.CODE);
621
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
622
                                String errorMessage = kxmlParser.nextText();
623
                                errors.add(errorCode + errorMessage);
624
                            }
625
                            break;
626
                        case KXmlParser.END_TAG:
627
                            break;
628

    
629
                     }
630
                     tag = kxmlParser.nextTag();
631
                 }
632
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
633
            }
634
        }
635
        catch(XmlPullParserException parser_ex){
636
            throw new IOException(parser_ex.getMessage());
637
        }
638
       
639
        String message = errors.size() > 0? "" : null;
640
        for (int i = 0; i < errors.size(); i++) {
641
            message += (String) errors.get(i)+"\n";
642
        }
643
        return message;
644
    }
645
    
646
 }