Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libRemoteServices / src / org / gvsig / remoteclient / wmts / WMTSProtocolHandler.java @ 37920

History | View | Annotate | Download (16.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.remoteclient.wmts;
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.FileOutputStream;
30
import java.io.FileReader;
31
import java.io.IOException;
32
import java.io.OutputStreamWriter;
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.StringTokenizer;
41
import java.util.prefs.Preferences;
42

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
416
                                if (token.toUpperCase().compareTo("REQUEST=GETCAPABILITIES") == 0)
417
                                        continue;
418

    
419
                                if (token.toUpperCase().compareTo("SERVICE=WMTS") == 0)
420
                                        continue;
421

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

    
427
                                if (token.toUpperCase().compareTo("EXCEPTIONS=XML") == 0)
428
                                        continue;
429

    
430
                                newQuery += token + "&";
431
                        }
432

    
433
                _host = host + newQuery;
434
                }
435
                else {
436
                        _host += "?";
437
                }
438

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

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

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