Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.h2spatial / org.gvsig.h2spatial.h2gis150 / org.gvsig.h2spatial.h2gis150.provider / src / main / java / org / gvsig / fmap / dal / store / h2 / H2SpatialHelper.java @ 45499

History | View | Annotate | Download (22.7 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 static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_NAME;
34
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_VALUE;
35
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_NAME;
36
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_RESOURCE;
37
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_CONFIGURATION_NAME;
38
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_RESOURCES_NAME;
39
import org.gvsig.fmap.dal.exception.InitializeException;
40
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
41
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
42
import org.gvsig.fmap.dal.store.h2.operations.H2SpatialOperationsFactory;
43
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
44
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
45
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
46
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
47
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException;
48
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
49
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
50
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
51
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase;
52
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
53
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverBase;
54
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverDumb;
55
import org.h2.tools.Server;
56
import org.h2gis.functions.factory.H2GISFunctions;
57
import org.h2gis.functions.system.H2GISversion;
58
import org.slf4j.Logger;
59
import org.slf4j.LoggerFactory;
60

    
61
@SuppressWarnings("UseSpecificCatch")
62
public class H2SpatialHelper extends JDBCHelperBase {
63

    
64
    static final Logger LOGGER = LoggerFactory.getLogger(H2SpatialHelper.class);
65

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

    
129
    public static class ConnectionProviderImpl implements ConnectionProvider {
130

    
131
        private static boolean needRegisterDriver = true;
132

    
133
        private BasicDataSource dataSource = null;
134

    
135
        private final H2SpatialConnectionParameters connectionParameters;
136
        
137
        private static Server server = null;
138
        private static boolean startServer = true;
139

    
140
        public ConnectionProviderImpl(H2SpatialConnectionParameters connectionParameters) {
141
            this.connectionParameters = connectionParameters;
142
        }
143

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

    
246
        }
247

    
248
        @Override
249
        public String toString() {
250
            StringBuilder builder = new StringBuilder();
251
            builder.append(" url=").append(connectionParameters.getUrl());
252
            builder.append(" driver name=").append(connectionParameters.getJDBCDriverClassName());
253
            builder.append(" user=").append(connectionParameters.getUser());
254
            return builder.toString();
255
        }
256
        
257
        @Override
258
        public synchronized Connection getConnection() throws SQLException {
259
            File f = H2SpatialHelper.getLocalFile(connectionParameters);
260
            boolean newdb = !f.exists();
261
            
262
            if (this.dataSource == null) {
263
                this.dataSource = this.createDataSource();               
264
            }
265
            Connection conn;
266
            try {
267
                conn = this.dataSource.getConnection();
268
            } catch(Throwable th) {
269
                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'. "+this.getStatus());
270
                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'.",th);
271
                throw th;
272
            }
273
            try {
274
                conn.createStatement().execute("SELECT TOP 1 SRID FROM SPATIAL_REF_SYS");
275
            } catch(SQLException ex) {
276
//                H2GISExtension.load(conn);
277
                H2GISFunctions.load(conn);
278
            }
279
            try {
280
                conn.createStatement().execute("CREATE SCHEMA IF NOT EXISTS PUBLIC;SET SCHEMA PUBLIC");
281
            } catch(SQLException ex) {
282
                LOGGER.trace("Can't create schema public.",ex);
283
                // Ignore this error.
284
            }
285
            if( newdb ) {
286
                    String[] sqls = new String[] {
287
                        "CREATE CACHED TABLE PUBLIC.\""+TABLE_RESOURCES_NAME+"\"(\""+FIELD_RESOURCES_NAME+"\" VARCHAR(150) NOT NULL, \""+FIELD_RESOURCES_RESOURCE+"\" BLOB DEFAULT NULL)",
288
                        "ALTER TABLE PUBLIC.\""+TABLE_RESOURCES_NAME+"\" ADD CONSTRAINT PUBLIC.CONSTRAINT_E PRIMARY KEY(\""+FIELD_RESOURCES_NAME+"\")",
289
                        "CREATE CACHED TABLE PUBLIC.\""+TABLE_CONFIGURATION_NAME+"\"(\""+FIELD_CONFIGURATION_NAME+"\" VARCHAR(200) NOT NULL, \""+FIELD_CONFIGURATION_VALUE+"\" VARCHAR(200) DEFAULT NULL)",
290
                        "ALTER TABLE PUBLIC.\""+TABLE_CONFIGURATION_NAME+"\" ADD CONSTRAINT PUBLIC.CONSTRAINT_2 PRIMARY KEY(\""+FIELD_CONFIGURATION_NAME+"\")"
291
                    };
292
                    for (String sql : sqls) {
293
                        try {
294
                            conn.createStatement().execute(sql);
295
                        } catch(SQLException ex) {
296
                            LOGGER.debug("Can't configure gvsig tables.",ex);
297
                            LOGGER.warn("Can't configure gvsig tables. "+sql);
298
                            // Ignore this error.
299
                        }
300
                    }
301
            }
302
            return conn;
303
        }
304
        
305
        private BasicDataSource createDataSource() throws SQLException {
306
            if (!this.isRegistered()) {
307
                this.registerDriver();
308
            }
309
            startServer();
310
            H2SpatialConnectionParameters params = connectionParameters;
311

    
312
            BasicDataSource ds = new BasicDataSource();
313
            ds.setDriverClassName(params.getJDBCDriverClassName());
314
            if( !StringUtils.isEmpty(params.getUser()) ) {
315
                ds.setUsername(params.getUser());
316
            }
317
            if( !StringUtils.isEmpty(params.getPassword()) ) {
318
                ds.setPassword(params.getPassword());
319
            }
320
            ds.setUrl(params.getUrl());
321

    
322
            ds.setMaxWait(60L * 1000);
323
            
324
            //
325
            // Ajustamos el pool para que las conexiones se cierren a los
326
            // 10 segundos, asi tratamos de que al salir de gvSIG no queden
327
            // conexiones abiertas con la BBDD y pueda quedar corrupta esta.
328
            // Hay que tener en cuenta que es una BBDD embebida, y mientras
329
            // hayan conexiones abiertas pueden quedar cosas por bajar a disco.
330
            //
331
            int sidle = this.connectionParameters.getMaxSecondsIdle();
332
            if( sidle < 0 ) {
333
                ds.setTimeBetweenEvictionRunsMillis(-1);
334
                ds.setMinEvictableIdleTimeMillis(30*1000);
335
            } else {
336
                // Revisamos las conexiones inactivas cada 10 segundos
337
                ds.setTimeBetweenEvictionRunsMillis(sidle*1000);
338
                // Eliminadmos las conexiones que lleven inactivas mas de 10 segundos.
339
                ds.setMinEvictableIdleTimeMillis(sidle*1000);
340
            }
341
            
342
            // Ajustamos el numero minimo de conexiones a 0 para permitir
343
            // que se lleguen a cerrar todas las conexiones del pool.
344
            ds.setMinIdle(0);
345
            // dejaremos el MaxIdle a 20, no parece importante. .
346
            ds.setMaxIdle(20);
347
            
348
            return ds;
349
        }
350

    
351
        private boolean isRegistered() {
352
            return needRegisterDriver;
353
        }
354

    
355
        @Override
356
        public void registerDriver() throws SQLException {
357
            String className = this.connectionParameters.getJDBCDriverClassName();
358
            if (className == null) {
359
                return;
360
            }
361
            try {
362
                Class theClass = Class.forName(className);
363
                if (theClass == null) {
364
                    throw new JDBCDriverClassNotFoundException(H2SpatialLibrary.NAME, className);
365
                }
366
            } catch (Exception e) {
367
                throw new SQLException("Can't register JDBC driver '" + className + "'.", e);
368
            }
369
            needRegisterDriver = false;
370
        }
371

    
372
    }
373

    
374
    private ConnectionProvider connectionProvider = null;
375
 
376
    /**
377
     * Constructor for use only for testing purposes.
378
     * 
379
     * @param connectionParameters
380
     * @param connectionProvider
381
     */
382
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters, ConnectionProvider connectionProvider) { 
383
        super(connectionParameters);
384
        this.srssolver = new SRSSolverDumb(this);
385
        this.connectionProvider = connectionProvider;
386
    }
387
  
388
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters) {
389
        super(connectionParameters);
390
        this.srssolver = new SRSSolverBase(this);
391
    }
392

    
393
    
394
    public void  shutdown() {
395
        try {
396
            if( this.connectionProvider!=null ) {
397
                ((ConnectionProviderImpl) this.connectionProvider).shutdown();
398
                this.connectionProvider = null;
399
            }
400
            ConnectionProviderImpl.stopServer();
401
        } catch (Throwable ex) {
402
            LOGGER.warn("Problems shutdown H2", ex);
403
        }
404
    }
405

    
406
    private void logConnectionStatus(String msg, Connection conn) {
407
        ConnectionProvider cp = this.getConnectionProvider();
408
        StringBuilder builder = new StringBuilder();
409
        builder.append(msg);
410
        if( conn == null ) {
411
            builder.append(": connection null");
412
        } else {
413
            Boolean closed = null;
414
            try {
415
                closed = conn.isClosed();
416
            } catch(Throwable th) {
417
            }
418
            builder.append(": connection ");
419
            builder.append(JDBCUtils.getConnId(conn));
420
            if( closed ) {
421
                builder.append(" (c)");
422
            }
423
            builder.append(" ");
424
        }
425
        builder.append(cp.getStatus());
426
        LOGGER.info(builder.toString());
427
    }
428
        
429
    private ConnectionProvider getConnectionProvider() {
430
        if (this.connectionProvider == null) {
431
          H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
432
          if( connectionParameters==null ) {
433
            return null; // Testing mode?
434
          }
435
          this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
436
        }
437
        return this.connectionProvider;
438
    }
439
    
440
    @Override
441
    public synchronized Connection  getConnection() throws AccessResourceException {
442
        try {
443
            if (this.connectionProvider == null) {
444
              H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
445
              if( connectionParameters==null ) {
446
                return null; // Testing mode?
447
              }
448
              this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
449
            }
450
            Connection connection = this.connectionProvider.getConnection();
451
            if( LOGGER.isDebugEnabled() ) {
452
                LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] getConnection "+connectionProvider.getStatus()+" "+ connectionProvider.toString());
453
            }
454
            return connection;
455
        } catch (SQLException ex) {
456
            throw new AccessResourceException(H2SpatialLibrary.NAME, ex);
457
        }
458
    }
459

    
460
    @Override
461
    public void closeConnection(Connection connection) {
462
      if( connection!=null ) { // In test ???
463
        LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] closeConnection "+ this.connectionProvider.getStatus());
464
      }
465
      super.closeConnection(connection);
466
    }
467
    
468
    @Override
469
    public H2SpatialConnectionParameters getConnectionParameters() {
470
        return (H2SpatialConnectionParameters) super.getConnectionParameters();
471
    }
472
    
473
    @Override
474
    public String getConnectionURL() {
475
        return getConnectionURL(this.getConnectionParameters());
476
    }
477

    
478
    @Override
479
    protected String getResourceType() {
480
        return H2SpatialLibrary.NAME;
481
    }
482

    
483
    @Override
484
    public String getProviderName() {
485
        return H2SpatialLibrary.NAME;
486
    }
487

    
488
    @Override
489
    public JDBCSQLBuilderBase createSQLBuilder() {
490
        return new H2SpatialSQLBuilder(this);
491
    }
492
    
493
    @Override
494
    public OperationsFactory getOperations() {
495
        if (this.operationsFactory == null) {
496
            this.operationsFactory = new H2SpatialOperationsFactory(this);
497
        }
498
        return operationsFactory;
499
    }
500

    
501
    @Override
502
    public GeometrySupportType getGeometrySupportType() {
503
        return GeometrySupportType.WKB;
504
    }
505

    
506
    @Override
507
    public boolean hasSpatialFunctions() {
508
        return true;
509
    }
510

    
511
    @Override
512
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
513
        return true;
514
    }
515

    
516
    @Override
517
    public String getQuoteForIdentifiers() {
518
        return "\"";
519
    }
520

    
521
    @Override
522
    public boolean allowAutomaticValues() {
523
        return true;
524
    }
525

    
526
    @Override
527
    public boolean supportOffsetInSelect() {
528
        return true;
529
    }
530

    
531
    @Override
532
    public String getQuoteForStrings() {
533
        return "'";
534
    }
535

    
536
    @Override
537
    public String getSourceId(JDBCStoreParameters parameters) {
538
        H2SpatialStoreParameters h2params = (H2SpatialStoreParameters) parameters;
539
        StringBuilder builder = new StringBuilder();
540
        builder.append(h2params.getTable());
541
        builder.append("(");
542
        if( StringUtils.isNotBlank(h2params.getHost()) ) {
543
            builder.append(h2params.getHost());
544
        }
545
        if( h2params.getPort()>0 ) {
546
            builder.append(",");
547
            builder.append(h2params.getPort());
548
        }
549
        File f = h2params.getFile();       
550
        if( f != null ) {
551
            builder.append(",");
552
            builder.append(h2params.getFile().getAbsolutePath());
553
        }
554
        builder.append(")");
555
        return builder.toString();
556
    }
557

    
558
    @Override
559
    public JDBCNewStoreParameters createNewStoreParameters() {
560
        return new H2SpatialNewStoreParameters();
561
    }
562

    
563
    @Override
564
    public JDBCStoreParameters createOpenStoreParameters() {
565
        return new H2SpatialStoreParameters();
566
    }
567

    
568
    @Override
569
    public JDBCServerExplorerParameters createServerExplorerParameters() {
570
        return new H2SpatialExplorerParameters();
571
    }
572

    
573
    @Override
574
    public JDBCServerExplorer createServerExplorer(
575
            JDBCServerExplorerParameters parameters, 
576
            DataServerExplorerProviderServices providerServices
577
        ) throws InitializeException {
578
        
579
        JDBCServerExplorer explorer = new H2SpatialExplorer(
580
                parameters, 
581
                providerServices, 
582
                this
583
        );
584
        this.initialize(explorer, parameters, null);
585
        return explorer;
586
    }
587
    
588
}