Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.remoteclient / src / main / java / org / gvsig / remoteclient / wmts / WMTSProtocolHandler.java @ 40769

History | View | Annotate | Download (16.9 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
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 3
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.remoteclient.wmts;
25

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

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

    
50
import org.gvsig.compat.net.ICancellable;
51
import org.gvsig.remoteclient.exceptions.ServerErrorException;
52
import org.gvsig.remoteclient.ogc.OGCProtocolHandler;
53
import org.gvsig.remoteclient.ogc.OGCServiceInformation;
54
import org.gvsig.remoteclient.utils.ExceptionTags;
55
import org.gvsig.remoteclient.utils.Utilities;
56
import org.gvsig.remoteclient.wmts.exception.DownloadException;
57
import org.gvsig.remoteclient.wmts.exception.WMTSException;
58
import org.gvsig.remoteclient.wmts.request.WMTSGetCapabilitiesRequest;
59
import org.gvsig.remoteclient.wmts.request.WMTSGetFeatureInfoRequest;
60
import org.gvsig.remoteclient.wmts.request.WMTSGetTileRequest;
61
import org.kxml2.io.KXmlParser;
62
import org.xmlpull.v1.XmlPullParserException;
63

    
64
/**
65
 * <p> Abstract class that represents handlers to comunicate via WMS protocol.</p>
66
 * 
67
 * @author Nacho Brodin (nachobrodin@gmail.com)
68
 */
69
public abstract class WMTSProtocolHandler extends OGCProtocolHandler {
70
        /**
71
         * Encoding used to parse different xml documents.
72
         */
73
        protected String                 encoding                     = "UTF-8";
74
    protected WMTSServiceInformation serviceInfo                  = null;
75
        private static int               count                        = 0;
76
        final String                     tempDirectoryPath            = System.getProperty("java.io.tmpdir")+"/tmp-andami";
77
        protected boolean                forceLongitudeFirstAxisOrder = false;
78
        
79
    protected abstract WMTSGetFeatureInfoRequest createGetFeatureInfoRequest(WMTSStatus status, int x, int y);
80

    
81
    protected abstract WMTSGetTileRequest createGetTileRequest(WMTSStatus status);
82
    
83
    protected abstract WMTSGetCapabilitiesRequest createGetCapabilitiesRequest();
84
    
85
    /**
86
         * Sets longitude first in the axis order read from the capabilities file
87
         * @param force
88
         */
89
        public void setForceLongitudeFirstAxisOrder(boolean force) {
90
                this.forceLongitudeFirstAxisOrder = force;
91
        }
92
        
93
    //**************************************************************
94
    //Esto es temporal hasta que est? la cach?
95
    
96
        private Hashtable                downloadedFiles;
97
    /**
98
         * Returns the content of a URL as a file from the file system.<br>
99
         * <p>
100
         * If the URL has been already downloaded in this session and notified
101
         * to the system using the static <b>Utilities.addDownloadedURL(URL)</b>
102
         * method, it can be restored faster from the file system avoiding to
103
         * download it again.
104
         * </p>
105
         * @param url
106
         * @return File containing this URL's content or null if no file was found.
107
         */
108
        private File getPreviousDownloaded(Object object) {
109
                File f = null;
110
                if (downloadedFiles != null && downloadedFiles.containsKey(object)) {
111
                        String filePath = (String) downloadedFiles.get(object);
112
                        f = new File(filePath);
113
                        if (!f.exists())
114
                                return null;
115
                }
116
                return f;
117
        }
118

    
119
        /**
120
         * Adds an URL to the table of downloaded files for further uses. If the URL
121
         * already exists in the table its filePath value is updated to the new one and
122
         * the old file itself is removed from the file system.
123
         *
124
         * @param url
125
         * @param filePath
126
         */
127
        void addDownloadedURL(URL url, String filePath) {
128
                if (downloadedFiles == null)
129
                        downloadedFiles = new Hashtable();
130
                String fileName = (String) downloadedFiles.put(url, filePath);
131
        }
132
        
133
        //Fin del c?digo temporal hasta que est? la cach?
134
        //**************************************************************
135
        
136
    /**
137
     * Sets the status object
138
     * @param status
139
     */
140
    protected abstract void setServerDescription(WMTSServerDescription status);
141
    
142
    public synchronized File getTile(WMTSStatus status, ICancellable cancel, File file) throws ServerErrorException, WMTSException {
143
            try {
144
                        WMTSGetTileRequest request = createGetTileRequest(status);
145
                        request.sendRequest(cancel, file);
146
                        try {
147
                                checkFileError(file);
148
                        } catch(WMTSException e) {
149
                                file.delete();
150
                                throw new WMTSException("This tile is not available:" + file.getAbsoluteFile().toString(), e);
151
                        } catch(IOException e) {
152
                                file.delete();
153
                                throw new WMTSException("This tile is not available:" + file.getAbsoluteFile().toString(), e);
154
                        }
155
                        return file;
156
                } catch(IOException e) {
157
            throw new WMTSException(e);
158
                }
159
    }
160
    
161
    public synchronized File getTile(WMTSStatus status, ICancellable cancel) throws ServerErrorException, WMTSException {
162
            try {
163
                        WMTSGetTileRequest request = createGetTileRequest(status);
164
                        File f = request.sendRequest(cancel);
165
                        try {
166
                                checkFileError(f);
167
                        } catch(WMTSException e) {
168
                                downloader.removeURL(request);
169
                    throw new ServerErrorException();
170
                        }
171
                        return f;
172
                } catch(IOException e) {
173
                        e.printStackTrace();
174
            throw new ServerErrorException();
175
                }
176
    }
177
    
178
    /**
179
     * Checks if the file downloaded is correct or doesn't
180
     * @param f
181
     * @throws WMTSException
182
     * @throws IOException
183
     */
184
    private void checkFileError(File f) throws WMTSException, IOException {
185
                if (f == null)
186
                    return;
187
            if (Utilities.isTextFile(f)) {
188
            byte[] data = fileToBytes(f);
189

    
190
                String exceptionMessage = parseException(data);
191
            if (exceptionMessage == null) {
192
                     String error = new String(data);
193
                    int pos = error.indexOf("<?xml");
194
                    if (pos!= -1) {
195
                            String xml = error.substring(pos,error.length());
196
                            exceptionMessage = parseException(xml.getBytes());
197
                    }
198
                if (exceptionMessage == null)
199
                        exceptionMessage = new String(data);
200

    
201
            }
202
            throw new WMTSException(exceptionMessage);
203
        }
204
    }
205
    
206
    public synchronized URL getTileURL(WMTSStatus status) throws MalformedURLException {
207
                String onlineResource = getHost();
208
                String symbol = getSymbol(onlineResource);
209
                onlineResource = onlineResource + symbol;
210
                
211
                StringBuffer req = new StringBuffer();
212
                req.append(onlineResource)
213
                .append("REQUEST=GetTile&SERVICE=WMTS&VERSION=").append(getVersion()).append("&")
214
                .append("Layer=" + status.getLayer())
215
        .append("&Style=" + status.getStyle())
216
        .append("&Format=" + status.getFormat())
217
        .append("&TileMatrixSet=" + status.getTileMatrixSet())
218
        .append("&TileMatrix=" + status.getTileMatrix())
219
        .append("&TileRow=" + status. getTileRow())
220
        .append("&TileCol=" + status.getTileCol());
221
                
222
                return new URL(req.toString());
223
    }
224
    
225
    /**
226
     * Gets a file name to download. I
227
     * @param url
228
     * @return
229
     */
230
    private File getFile() {
231
            count ++;
232
                int index = name.lastIndexOf(".");
233
                if (index > 0){
234
                        return new File(tempDirectoryPath + "/" + name.substring(0,index) + System.currentTimeMillis() + count + 
235
                                name.substring(index, name.length()));
236
                }
237
                return new File(tempDirectoryPath + "/" + name + System.currentTimeMillis() + count);
238
    }
239
    
240
    public synchronized File downloadFile(URL url, ICancellable cancel) throws DownloadException {
241
            File f = null;
242
            if((f = getPreviousDownloaded(url)) != null)
243
                    return f;
244
                    
245
            Preferences prefs = Preferences.userRoot().node( "gvsig.downloader" );
246
            int IO_BUFFER_SIZE = 8 * 1024;
247
                int timeout = prefs.getInt("timeout", 60000);
248
                
249
                File dstFile = getFile();
250
                
251
                DataOutputStream dos;
252
                try {
253
                        DataInputStream is;
254
                        OutputStreamWriter os = null;
255
                        HttpURLConnection connection = null;
256

    
257
                        if (url.getProtocol().equals("https")) {
258
                                disableHttsValidation();
259
                        }
260
                        connection = (HttpURLConnection)url.openConnection();
261
                        connection.setConnectTimeout(timeout);
262
                        
263
                        is = new DataInputStream(url.openStream());
264
                        
265
                        dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(dstFile)));
266
                        byte[] buffer = new byte[IO_BUFFER_SIZE];
267

    
268
                        int read;
269
                        while ((read = is.read(buffer)) != -1) {
270
                                dos.write(buffer, 0, read);
271
                        }
272
                        
273
                        if(os != null) {
274
                                os.close();
275
                        }
276
                        dos.close();
277
                        is.close();
278
                        is = null;
279
                        dos = null;
280
                        if (cancel != null && cancel.isCanceled()) {
281
                                dstFile.delete();
282
                                dstFile = null;
283
                                return null;
284
                        }
285
                } catch (Exception e) {
286
                        throw new DownloadException(e);
287
                }
288
                addDownloadedURL(url, dstFile.getAbsolutePath());
289
                return dstFile;
290
    }
291
    
292
    /**
293
         * This method disables the Https certificate validation.
294
         * @throws KeyManagementException
295
         * @throws NoSuchAlgorithmException
296
         */
297
        private void disableHttsValidation() throws KeyManagementException, NoSuchAlgorithmException{
298
                // Create a trust manager that does not validate certificate chains
299
                TrustManager[] trustAllCerts = new TrustManager[] {
300
                                new X509TrustManager() {
301
                                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
302
                                                return null;
303
                                        }
304
                                        public void checkClientTrusted(
305
                                                        java.security.cert.X509Certificate[] certs, String authType) {
306
                                        }
307
                                        public void checkServerTrusted(
308
                                                        java.security.cert.X509Certificate[] certs, String authType) {
309
                                        }
310
                                }
311
                };
312

    
313
                // Install the all-trusting trust manager
314
                SSLContext sc = SSLContext.getInstance("SSL");
315
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
316
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
317
        }
318
    
319
        /**
320
     * <p>It will send a GetFeatureInfo request to the WMTS
321
     * Parsing the response and redirecting the info to the WMTS client</p>
322
     * TODO: return a stored file instead a String.
323
     */
324
    public String getFeatureInfo(WMTSStatus status, int x, int y, ICancellable cancel) {
325
            StringBuffer output = new StringBuffer();
326
            String outputFormat = new String();
327
            String ServiceException = "ServiceExceptionReport";
328
            StringBuffer sb = new StringBuffer();
329
            sb.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
330
                try {
331
                        WMTSGetFeatureInfoRequest request = createGetFeatureInfoRequest(status, x, y);
332
                        URL url = request.getURL();
333
                    outputFormat = url.openConnection().getContentType();
334
                    File f = request.sendRequest(cancel);
335
                        if (f == null) {
336
                                return "";
337
                        }
338

    
339
                        FileReader fReader = new FileReader(f);
340
                        char[] buffer = new char[1024*256];
341
                        for (int i = fReader.read(buffer); i > 0; i = fReader.read(buffer)) {
342
                            String str = new String(buffer,0,i);
343
                            output.append(str);
344
                    }
345
                    if ( (outputFormat == null) || (outputFormat.indexOf("xml") != -1)
346
                                    ||output.toString().toLowerCase().startsWith("<?xml")
347
                                    ||(outputFormat.indexOf("gml") != -1)) {
348
                            KXmlParser kxmlParser = null;
349
                            kxmlParser = new KXmlParser();
350
                            //kxmlParser.setInput(new StringReader(output.toString()));
351
                            kxmlParser.setInput(new FileReader(f));
352

    
353
                            kxmlParser.nextTag();
354
                            if (kxmlParser.getName().compareTo(ServiceException) == 0) {
355
                                    sb.append("<INFO>").append(parseException( output.toString().getBytes())).append("</INFO>");
356
                                    return sb.toString();
357
                                } else if (kxmlParser.getName().compareToIgnoreCase("ERROR") == 0) {
358
                                        return output.toString();
359
                                } else {
360
                                        return output.toString();
361
                                }
362
                    } else {                  
363
                             return output.toString();
364
                    }
365
                } catch(XmlPullParserException parserEx) {
366
                    if (output.toString().toLowerCase().indexOf("xml") != -1) {
367
                            return output.toString().trim();
368
                    } else {
369
                               sb.append("<INFO>").append("Info format not supported").append("</INFO>");
370
                        return sb.toString();
371
                    }
372
            } catch(Exception e) {
373
                    e.printStackTrace();
374
                    sb.append("<INFO>").append("Info format not supported").append("</INFO>");
375
                    return sb.toString();
376
            }
377
    }
378

    
379
    /**
380
         * <p>Builds a GetCapabilities request that is sent to the WMS
381
         * the response will be parse to extract the data needed by the
382
         * WMS client</p>
383
         * @param override, if true the previous downloaded data will be overridden
384
         */
385
    public void getCapabilities(WMTSServerDescription status, boolean override, ICancellable cancel) {
386
            try {
387
                    setServerDescription(status);
388
                        WMTSGetCapabilitiesRequest request = createGetCapabilitiesRequest();
389
                        File f = request.sendRequest(cancel);                        
390
        
391
                        if (f == null)
392
                                return;
393
                        parseCapabilities(f);
394
            } catch(Exception e) {
395
                        e.printStackTrace();
396
                }
397
    }
398
    
399
    
400
    /**
401
     * Builds the GetCapabilitiesRequest according to the OGC WMS Specifications
402
     * without a VERSION, to get the highest version than a WMS supports.
403
     */
404
    public static String buildCapabilitiesSuitableVersionRequest(String _host, String _version) {
405
                int index = _host.indexOf('?');
406
                
407
                if (index > -1) {
408
                        String host = _host.substring(0, index + 1);
409
                        String query = _host.substring(index + 1, _host.length());
410
                        
411
                        StringTokenizer tokens = new StringTokenizer(query, "&");
412
                        String newQuery = "", token;
413

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

    
418
                                if (token.toUpperCase().compareTo("REQUEST=GETCAPABILITIES") == 0)
419
                                        continue;
420

    
421
                                if (token.toUpperCase().compareTo("SERVICE=WMTS") == 0)
422
                                        continue;
423

    
424
                                if ((_version != null) && (_version.length() > 0)) {
425
                                    if (token.toUpperCase().compareTo("VERSION=" + _version) == 0)
426
                                            continue;
427
                                }
428

    
429
                                if (token.toUpperCase().compareTo("EXCEPTIONS=XML") == 0)
430
                                        continue;
431

    
432
                                newQuery += token + "&";
433
                        }
434

    
435
                _host = host + newQuery;
436
                }
437
                else {
438
                        _host += "?";
439
                }
440

    
441
            if ((_version != null) && (_version.compareTo("") != 0))
442
                    _host += "REQUEST=GetCapabilities&SERVICE=WMTS&VERSION=" + _version;
443
            else
444
                    _host += "REQUEST=GetCapabilities&SERVICE=WMTS";
445

    
446
            return _host;
447
    }
448
    
449
   /*
450
    * (non-Javadoc)
451
    * @see org.gvsig.remoteclient.ogc.OGCProtocolHandler#getServiceInformation()
452
    */
453
    public OGCServiceInformation getServiceInformation() {
454
        return serviceInfo;
455
    }
456
    
457
        protected String parseException(byte[] data) throws IOException {
458
        ArrayList errors = new ArrayList();
459
        KXmlParser kxmlParser = new KXmlParser();
460
        try
461
        {
462
            kxmlParser.setInput(new ByteArrayInputStream(data), encoding);
463
            kxmlParser.nextTag();
464
            int tag;
465
            if ( kxmlParser.getEventType() != KXmlParser.END_DOCUMENT )
466
            {
467
                kxmlParser.require(KXmlParser.START_TAG, null, ExceptionTags.EXCEPTION_ROOT);
468
                tag = kxmlParser.nextTag();
469
                 while(tag != KXmlParser.END_DOCUMENT)
470
                 {
471
                     switch(tag)
472
                     {
473
                        case KXmlParser.START_TAG:
474
                            if (kxmlParser.getName().compareTo(ExceptionTags.SERVICE_EXCEPTION)==0){
475
                                String errorCode = kxmlParser.getAttributeValue("", ExceptionTags.CODE);
476
                                errorCode = (errorCode != null) ? "["+errorCode+"] " : "";
477
                                String errorMessage = kxmlParser.nextText();
478
                                errors.add(errorCode+errorMessage);
479
                            }
480
                            break;
481
                        case KXmlParser.END_TAG:
482
                            break;
483

    
484
                     }
485
                     tag = kxmlParser.nextTag();
486
                 }
487
                 //kxmlParser.require(KXmlParser.END_DOCUMENT, null, null);
488
            }
489
        }
490
        catch(XmlPullParserException parser_ex){
491
            throw new IOException(parser_ex.getMessage());
492
        }
493
       
494
        String message = errors.size()>0? "" : null;
495
        for (int i = 0; i < errors.size(); i++) {
496
            message += (String) errors.get(i)+"\n";
497
        }
498
        return message;
499
    }
500
    
501
 }