Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.h2 / src / main / java / org / gvsig / fmap / dal / store / h2 / H2SpatialHelper.java @ 45097

History | View | Annotate | Download (19.9 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2020 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 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
 */
22
package org.gvsig.fmap.dal.store.h2;
23

    
24
import org.gvsig.fmap.dal.store.jdbc2.spi.ConnectionProvider;
25
import java.io.File;
26
import java.sql.Connection;
27
import java.sql.SQLException;
28
import java.text.MessageFormat;
29
import org.apache.commons.dbcp.BasicDataSource;
30
import org.apache.commons.io.FilenameUtils;
31
import org.apache.commons.lang3.StringUtils;
32
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
33
import org.gvsig.fmap.dal.exception.InitializeException;
34
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
35
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
36
import org.gvsig.fmap.dal.store.h2.operations.H2SpatialOperationsFactory;
37
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
38
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
39
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
40
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
41
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException;
42
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
43
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
44
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
45
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase;
46
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
47
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverBase;
48
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverDumb;
49
import org.h2.tools.Server;
50
import org.h2gis.ext.H2GISExtension;
51
import org.slf4j.Logger;
52
import org.slf4j.LoggerFactory;
53

    
54
@SuppressWarnings("UseSpecificCatch")
55
public class H2SpatialHelper extends JDBCHelperBase {
56

    
57
    static final Logger LOGGER = LoggerFactory.getLogger(H2SpatialHelper.class);
58

    
59
    public static final String H2SPATIAL_JDBC_DRIVER = "org.h2.Driver";
60
    
61
    private static File getLocalFile(H2SpatialConnectionParameters params) {
62
        String host = params.getHost();
63
        if( !StringUtils.isEmpty(host) ) {
64
          host = host.toLowerCase().trim();
65
          if( !(host.equals("localhost") || host.equals("127.0.0.1")) ) {
66
            return null;
67
          }
68
        }
69
        File f = params.getFile();
70
        if( f == null ) {
71
          return null;
72
        }
73
        String pathname = f.getAbsolutePath().replace("\\","/");
74
        if( !pathname.endsWith(".mv.db")  ) {
75
          pathname += ".mv.db";
76
        }      
77
        
78
        return new File(pathname);
79
    }
80
    
81
    public static String getConnectionURL(H2SpatialConnectionParameters params) {
82
        String connectionURL;
83
        String dbfilename = params.getFile().getAbsolutePath().replace("\\","/");
84
        if( dbfilename!=null && dbfilename.endsWith(".mv.db") ) {
85
            dbfilename = dbfilename.substring(0, dbfilename.length()-6);
86
        }
87
        StringBuilder commonParameters = new StringBuilder();
88
        commonParameters.append(";MODE=PostgreSQL");
89
        commonParameters.append(";SCHEMA=PUBLIC");
90
        commonParameters.append(";ALLOW_LITERALS=ALL");
91
        if( StringUtils.isEmpty(params.getHost()) ) {
92
            // Asumimos que es una conexion directa sobre el filesystem
93
            if( StringUtils.equalsIgnoreCase(FilenameUtils.getExtension(params.getFile().getName()),"zip") ) {
94
                connectionURL =  MessageFormat.format(
95
                    "jdbc:h2:zip:{0}!/{1}"+commonParameters.toString(),
96
                    dbfilename,
97
                    params.getDBName()
98
                );
99
            } else {
100
                connectionURL =  MessageFormat.format(
101
                    "jdbc:h2:file:{0}"+commonParameters.toString(),
102
                    dbfilename
103
                );
104
            }
105
        } else if( params.getPort() == null ) {
106
            connectionURL =  MessageFormat.format(
107
                "jdbc:h2:tcp://{0}/{1}"+commonParameters.toString(),
108
                params.getHost(),
109
                dbfilename
110
            );            
111
        } else {
112
            connectionURL =  MessageFormat.format("jdbc:h2:tcp://{0}:{1,number,#######}/{2}"+commonParameters.toString(),
113
                params.getHost(),
114
                (int) params.getPort(),
115
                dbfilename
116
            );
117
        }
118
        LOGGER.debug("connectionURL: {}", connectionURL);
119
        return connectionURL;
120
    }
121

    
122
    public static class ConnectionProviderImpl implements ConnectionProvider {
123

    
124
        private static boolean needRegisterDriver = true;
125

    
126
        private BasicDataSource dataSource = null;
127

    
128
        private final H2SpatialConnectionParameters connectionParameters;
129
        
130
        private static Server server = null;
131
        private static boolean startServer = true;
132

    
133
        public ConnectionProviderImpl(H2SpatialConnectionParameters connectionParameters) {
134
            this.connectionParameters = connectionParameters;
135
        }
136

    
137
        @Override
138
        public String getStatus() {
139
            StringBuilder builder = new StringBuilder();
140
            builder.append("Pool: ");
141
            builder.append(JDBCUtils.getHexId(dataSource));
142
            builder.append(" Actives: ");
143
            builder.append(dataSource.getNumActive());
144
            builder.append("/");
145
            builder.append(dataSource.getMaxActive());
146
            builder.append(" idle: ");
147
            builder.append(dataSource.getNumIdle());
148
            builder.append("/");
149
            builder.append(dataSource.getMinIdle());
150
            builder.append(":");
151
            builder.append(dataSource.getMaxIdle());
152
            return builder.toString();
153
        }
154
        
155
        @SuppressWarnings("ConvertToTryWithResources")
156
        public void shutdown() {
157
            LOGGER.info("Shutdown H2 connection.");
158
            try {
159
                Connection conn = this.getConnection();
160
                conn.createStatement().execute("SHUTDOWN");
161
                conn.close();
162
            } catch (Throwable th) {
163
                LOGGER.warn("Problems shutdown the database.", th);
164
            }
165
            try {
166
                if( dataSource!=null ) {
167
                    LOGGER.info("Clossing connection pool.");
168
                    LOGGER.info(this.getStatus());
169
                    dataSource.close();
170
                    LOGGER.info("Connection pool closed.");
171
                    LOGGER.info(this.getStatus());
172
                }
173
            } catch (Throwable th) {
174
                LOGGER.warn("Problems closing connections pool.", th);
175
            }
176
        }
177
        
178
        public static void stopServer() {
179
            if (server == null) {
180
                LOGGER.info("The H2 server is already stopped.");
181
            } else {
182
                LOGGER.info("Stopping the H2 server.");
183
                LOGGER.info("  port  :" + server.getPort());
184
                LOGGER.info("  URL   :" + server.getURL());
185
                LOGGER.info("  shutdown server...");
186
                try {
187
                    server.shutdown();
188
                } catch (Throwable th) {
189
                    LOGGER.warn("Problems shutdown the H2 server.", th);
190
                }
191
                LOGGER.info("  Stoping server...");
192
                try {
193
                    server.stop();
194
                } catch (Throwable th) {
195
                    LOGGER.warn("Problems stopping the H2 server.", th);
196
                }
197
                LOGGER.info("  status:" + server.getStatus());
198
                server = null;
199
                LOGGER.info("H2 Server stopped");
200
            }
201
            startServer = true;
202
        }
203
        
204
        private void startServer() {
205
        
206
            if( startServer && server == null ) {
207
                String port = "9123";
208
                try {
209
                    Server theServer;
210
                    if( this.connectionParameters.getServerPort()>0 ) {
211
                        port = String.valueOf(this.connectionParameters.getServerPort());
212
                    }
213
                    if( this.connectionParameters.getServerAllowOthers() ) {
214
                        theServer = Server.createTcpServer("-tcpPort", port, "-ifExists", "-tcpAllowOthers");
215
                    } else {
216
                        theServer = Server.createTcpServer("-tcpPort", port, "-ifExists");
217
                    }
218
                    theServer.start();
219
                    server = theServer;
220
                    LOGGER.info("H2 Server started" );
221
                    LOGGER.info("  port  :"+ server.getPort());
222
                    LOGGER.info("  URL   :"+ server.getURL());
223
                    LOGGER.info("  status:"+ server.getStatus());
224
                    Runtime.getRuntime().addShutdownHook(new Thread() {
225
                        @Override
226
                        public void run() {
227
                            stopServer();
228
                        }
229
                    });
230
                } catch (SQLException ex) {
231
                    LOGGER.warn("H2 Server not started",ex);
232
                }
233
                // Tanto si consigue lanzar el server como si no, no lo vuelve a intentar
234
                startServer = false;
235
            }
236

    
237
        }
238

    
239
        @Override
240
        public String toString() {
241
            StringBuilder builder = new StringBuilder();
242
            builder.append(" url=").append(connectionParameters.getUrl());
243
            builder.append(" driver name=").append(connectionParameters.getJDBCDriverClassName());
244
            builder.append(" user=").append(connectionParameters.getUser());
245
            return builder.toString();
246
        }
247
        
248
        @Override
249
        public Connection getConnection() throws SQLException {
250
            if (this.dataSource == null) {
251
                this.dataSource = this.createDataSource();               
252
            }
253
            Connection conn;
254
            try {
255
                conn = this.dataSource.getConnection();
256
            } catch(Throwable th) {
257
                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'. "+this.getStatus());
258
                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'.",th);
259
                throw th;
260
            }
261
            try {
262
                conn.createStatement().execute("SELECT TOP 1 SRID FROM SPATIAL_REF_SYS");
263
            } catch(SQLException ex) {
264
                H2GISExtension.load(conn);
265
            }
266
            try {
267
                conn.createStatement().execute("CREATE SCHEMA IF NOT EXISTS PUBLIC;SET SCHEMA PUBLIC");
268
            } catch(SQLException ex) {
269
                LOGGER.trace("Can't create schema public.",ex);
270
                // Ignore this error.
271
            }
272
            return conn;
273
        }
274

    
275
        private BasicDataSource createDataSource() throws SQLException {
276
            if (!this.isRegistered()) {
277
                this.registerDriver();
278
            }
279
            startServer();
280
            H2SpatialConnectionParameters params = connectionParameters;
281

    
282
            BasicDataSource ds = new BasicDataSource();
283
            ds.setDriverClassName(params.getJDBCDriverClassName());
284
            if( !StringUtils.isEmpty(params.getUser()) ) {
285
                ds.setUsername(params.getUser());
286
            }
287
            if( !StringUtils.isEmpty(params.getPassword()) ) {
288
                ds.setPassword(params.getPassword());
289
            }
290
            ds.setUrl(params.getUrl());
291

    
292
            ds.setMaxWait(60L * 1000);
293
            
294
            //
295
            // Ajustamos el pool para que las conexiones se cierren a los
296
            // 10 segundos, asi tratamos de que al salir de gvSIG no queden
297
            // conexiones abiertas con la BBDD y pueda quedar corrupta esta.
298
            // Hay que tener en cuenta que es una BBDD embebida, y mientras
299
            // hayan conexiones abiertas pueden quedar cosas por bajar a disco.
300
            //
301
            int sidle = this.connectionParameters.getMaxSecondsIdle();
302
            if( sidle < 0 ) {
303
                ds.setTimeBetweenEvictionRunsMillis(-1);
304
                ds.setMinEvictableIdleTimeMillis(30*1000);
305
            } else {
306
                // Revisamos las conexiones inactivas cada 10 segundos
307
                ds.setTimeBetweenEvictionRunsMillis(sidle*1000);
308
                // Eliminadmos las conexiones que lleven inactivas mas de 10 segundos.
309
                ds.setMinEvictableIdleTimeMillis(sidle*1000);
310
            }
311
            
312
            // Ajustamos el numero minimo de conexiones a 0 para permitir
313
            // que se lleguen a cerrar todas las conexiones del pool.
314
            ds.setMinIdle(0);
315
            // dejaremos el MaxIdle a 20, no parece importante. .
316
            ds.setMaxIdle(20);
317
            
318
            return ds;
319
        }
320

    
321
        private boolean isRegistered() {
322
            return needRegisterDriver;
323
        }
324

    
325
        @Override
326
        public void registerDriver() throws SQLException {
327
            String className = this.connectionParameters.getJDBCDriverClassName();
328
            if (className == null) {
329
                return;
330
            }
331
            try {
332
                Class theClass = Class.forName(className);
333
                if (theClass == null) {
334
                    throw new JDBCDriverClassNotFoundException(H2SpatialLibrary.NAME, className);
335
                }
336
            } catch (Exception e) {
337
                throw new SQLException("Can't register JDBC driver '" + className + "'.", e);
338
            }
339
            needRegisterDriver = false;
340
        }
341

    
342
    }
343

    
344
    private ConnectionProvider connectionProvider = null;
345
 
346
    /**
347
     * Constructor for use only for testing purposes.
348
     * 
349
     * @param connectionParameters
350
     * @param connectionProvider
351
     */
352
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters, ConnectionProvider connectionProvider) { 
353
        super(connectionParameters);
354
        this.srssolver = new SRSSolverDumb(this);
355
        this.connectionProvider = connectionProvider;
356
    }
357
  
358
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters) {
359
        super(connectionParameters);
360
        this.srssolver = new SRSSolverBase(this);
361
    }
362

    
363
    
364
    public void  shutdown() {
365
        try {
366
            if( this.connectionProvider!=null ) {
367
                ((ConnectionProviderImpl) this.connectionProvider).shutdown();
368
                this.connectionProvider = null;
369
            }
370
            ConnectionProviderImpl.stopServer();
371
        } catch (Throwable ex) {
372
            LOGGER.warn("Problems shutdown H2", ex);
373
        }
374
    }
375

    
376
    private void logConnectionStatus(String msg, Connection conn) {
377
        ConnectionProvider cp = this.getConnectionProvider();
378
        StringBuilder builder = new StringBuilder();
379
        builder.append(msg);
380
        if( conn == null ) {
381
            builder.append(": connection null");
382
        } else {
383
            Boolean closed = null;
384
            try {
385
                closed = conn.isClosed();
386
            } catch(Throwable th) {
387
            }
388
            builder.append(": connection ");
389
            builder.append(JDBCUtils.getConnId(conn));
390
            if( closed ) {
391
                builder.append(" (c)");
392
            }
393
            builder.append(" ");
394
        }
395
        builder.append(cp.getStatus());
396
        LOGGER.info(builder.toString());
397
    }
398
        
399
    private ConnectionProvider getConnectionProvider() {
400
        if (this.connectionProvider == null) {
401
          H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
402
          if( connectionParameters==null ) {
403
            return null; // Testing mode?
404
          }
405
          this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
406
        }
407
        return this.connectionProvider;
408
    }
409
    
410
    @Override
411
    public synchronized Connection  getConnection() throws AccessResourceException {
412
        try {
413
            if (this.connectionProvider == null) {
414
              H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
415
              if( connectionParameters==null ) {
416
                return null; // Testing mode?
417
              }
418
              this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
419
            }
420
            Connection connection = this.connectionProvider.getConnection();
421
            if( LOGGER.isDebugEnabled() ) {
422
                LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] getConnection "+connectionProvider.getStatus()+" "+ connectionProvider.toString());
423
            }
424
            return connection;
425
        } catch (SQLException ex) {
426
            throw new AccessResourceException(H2SpatialLibrary.NAME, ex);
427
        }
428
    }
429

    
430
    @Override
431
    public void closeConnection(Connection connection) {
432
      if( connection!=null ) { // In test ???
433
        LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] closeConnection "+ this.connectionProvider.getStatus());
434
      }
435
      super.closeConnection(connection);
436
    }
437
    
438
    @Override
439
    public H2SpatialConnectionParameters getConnectionParameters() {
440
        return (H2SpatialConnectionParameters) super.getConnectionParameters();
441
    }
442
    
443
    @Override
444
    public String getConnectionURL() {
445
        return getConnectionURL(this.getConnectionParameters());
446
    }
447

    
448
    @Override
449
    protected String getResourceType() {
450
        return H2SpatialLibrary.NAME;
451
    }
452

    
453
    @Override
454
    public String getProviderName() {
455
        return H2SpatialLibrary.NAME;
456
    }
457

    
458
    @Override
459
    public JDBCSQLBuilderBase createSQLBuilder() {
460
        return new H2SpatialSQLBuilder(this);
461
    }
462
    
463
    @Override
464
    public OperationsFactory getOperations() {
465
        if (this.operationsFactory == null) {
466
            this.operationsFactory = new H2SpatialOperationsFactory(this);
467
        }
468
        return operationsFactory;
469
    }
470

    
471
    @Override
472
    public GeometrySupportType getGeometrySupportType() {
473
        return GeometrySupportType.WKB;
474
    }
475

    
476
    @Override
477
    public boolean hasSpatialFunctions() {
478
        return true;
479
    }
480

    
481
    @Override
482
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
483
        return true;
484
    }
485

    
486
    @Override
487
    public String getQuoteForIdentifiers() {
488
        return "\"";
489
    }
490

    
491
    @Override
492
    public boolean allowAutomaticValues() {
493
        return true;
494
    }
495

    
496
    @Override
497
    public boolean supportOffsetInSelect() {
498
        return true;
499
    }
500

    
501
    @Override
502
    public String getQuoteForStrings() {
503
        return "'";
504
    }
505

    
506
    @Override
507
    public String getSourceId(JDBCStoreParameters parameters) {
508
        return parameters.getDBName() + "." + 
509
               parameters.getSchema()+ "." + 
510
               parameters.getTable();
511
    }
512

    
513
    @Override
514
    public JDBCNewStoreParameters createNewStoreParameters() {
515
        return new H2SpatialNewStoreParameters();
516
    }
517

    
518
    @Override
519
    public JDBCStoreParameters createOpenStoreParameters() {
520
        return new H2SpatialStoreParameters();
521
    }
522

    
523
    @Override
524
    public JDBCServerExplorerParameters createServerExplorerParameters() {
525
        return new H2SpatialExplorerParameters();
526
    }
527

    
528
    @Override
529
    public JDBCServerExplorer createServerExplorer(
530
            JDBCServerExplorerParameters parameters, 
531
            DataServerExplorerProviderServices providerServices
532
        ) throws InitializeException {
533
        
534
        JDBCServerExplorer explorer = new H2SpatialExplorer(
535
                parameters, 
536
                providerServices, 
537
                this
538
        );
539
        this.initialize(explorer, parameters, null);
540
        return explorer;
541
    }
542
    
543
}