Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libRemoteServices / src / org / gvsig / remoteclient / wmts / WMTSProtocolHandler.java @ 35471

History | View | Annotate | Download (17 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
                                //We can throw an exception if a tile is wrong because the program crashes
149
                                System.err.println("This tile is not available:" + file.getAbsoluteFile().toString());
150
                        } catch(IOException e) {
151
                                file.delete();
152
                                //We can throw an exception if a tile is wrong because the program crashes
153
                                System.err.println("This tile is not available:" + file.getAbsoluteFile().toString());
154
                        }
155
                        return file;
156
                } catch(IOException e) {
157
                        e.printStackTrace();
158
            throw new ServerErrorException();
159
                }
160
    }
161
    
162
    public synchronized File getTile(WMTSStatus status, ICancellable cancel) throws ServerErrorException, WMTSException {
163
            try {
164
                        WMTSGetTileRequest request = createGetTileRequest(status);
165
                        File f = request.sendRequest(cancel);
166
                        try {
167
                                checkFileError(f);
168
                        } catch(WMTSException e) {
169
                                downloader.removeURL(request);
170
                    throw new ServerErrorException();
171
                        }
172
                        return f;
173
                } catch(IOException e) {
174
                        e.printStackTrace();
175
            throw new ServerErrorException();
176
                }
177
    }
178
    
179
    /**
180
     * Checks if the file downloaded is correct or doesn't
181
     * @param f
182
     * @throws WMTSException
183
     * @throws IOException
184
     */
185
    private void checkFileError(File f) throws WMTSException, IOException {
186
                if (f == null)
187
                    return;
188
            if (Utilities.isTextFile(f)) {
189
            byte[] data = fileToBytes(f);
190

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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