Statistics
| Revision:

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

History | View | Annotate | Download (20.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 java.io.File;
25
import java.sql.Connection;
26
import java.sql.DriverManager;
27
import java.sql.SQLException;
28
import org.apache.commons.dbcp.BasicDataSource;
29
import org.apache.commons.lang3.StringUtils;
30
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
31
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_NAME;
32
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_CONFIGURATION_VALUE;
33
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_NAME;
34
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.FIELD_RESOURCES_RESOURCE;
35
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_CONFIGURATION_NAME;
36
import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_RESOURCES_NAME;
37
import org.gvsig.fmap.dal.exception.InitializeException;
38
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
39
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
40
import org.gvsig.fmap.dal.spi.DataTransactionServices;
41
import static org.gvsig.fmap.dal.store.h2.H2SpatialHelper.LOGGER;
42
import org.gvsig.fmap.dal.store.h2.functions.Json_value;
43
import org.gvsig.fmap.dal.store.h2.functions.Reverse;
44
import org.gvsig.fmap.dal.store.h2.functions.Reverseinstr;
45
import org.gvsig.fmap.dal.store.h2.operations.H2SpatialOperationsFactory;
46
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
47
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
48
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
49
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
50
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException;
51
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
52
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
53
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
54
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
55
import org.gvsig.fmap.dal.store.jdbc2.spi.AbstractConnectionProvider;
56
import org.gvsig.fmap.dal.store.jdbc2.spi.ConnectionProvider;
57
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCConnectionBase;
58
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase;
59
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
60
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverDumb;
61
import org.h2gis.functions.factory.H2GISFunctions;
62
import org.slf4j.Logger;
63
import org.slf4j.LoggerFactory;
64

    
65

    
66
@SuppressWarnings("UseSpecificCatch")
67
public class H2SpatialHelper extends JDBCHelperBase {
68

    
69
    static final Logger LOGGER = LoggerFactory.getLogger(H2SpatialHelper.class);
70

    
71
    public static final String H2SPATIAL_JDBC_DRIVER = "org.h2.Driver";
72
    
73
    public static class ConnectionProviderImpl extends AbstractConnectionProvider implements ConnectionProvider {
74

    
75
        private static boolean needRegisterDriver = true;
76

    
77
        private BasicDataSource dataSource = null;
78

    
79
        private H2SpatialConnectionParameters connectionParameters;
80
        
81
        public ConnectionProviderImpl(H2SpatialConnectionParameters connectionParameters) {
82
            this.connectionParameters = connectionParameters;
83
        }
84

    
85
        @Override
86
        public String getStatus() {
87
            if(dataSource == null) {
88
                return "Not using pool.";
89
            }
90
            StringBuilder builder = new StringBuilder();
91
            builder.append("Pool: ");
92
            builder.append(JDBCUtils.getHexId(dataSource));
93
            builder.append(" Actives: ");
94
            builder.append(dataSource.getNumActive());
95
            builder.append("/");
96
            builder.append(dataSource.getMaxActive());
97
            builder.append(" idle: ");
98
            builder.append(dataSource.getNumIdle());
99
            builder.append("/");
100
            builder.append(dataSource.getMinIdle());
101
            builder.append(":");
102
            builder.append(dataSource.getMaxIdle());
103
            return builder.toString();
104
        }
105
        
106
        @SuppressWarnings("ConvertToTryWithResources")
107
        public void shutdown() {
108
            LOGGER.info("Shutdown H2 connection.");
109
            try {
110
                Connection conn = this.getConnection();
111
                conn.createStatement().execute("CHECKPOINT SYNC");
112
                conn.createStatement().execute("ANALYZE");
113
                conn.createStatement().execute("SHUTDOWN COMPACT");
114
                conn.close();
115
                LOGGER.info("Shutdown completed.");
116
            } catch (Throwable th) {
117
                LOGGER.warn("Problems shutdown the database.", th);
118
            }
119
            closeDataSource();
120
        }
121
        
122
        @Override
123
        public String toString() {
124
            StringBuilder builder = new StringBuilder();
125
            builder.append(" url=").append(connectionParameters.getUrl());
126
            builder.append(" driver name=").append(connectionParameters.getJDBCDriverClassName());
127
            builder.append(" user=").append(connectionParameters.getUser());
128
            return builder.toString();
129
        }
130
        
131
        @Override
132
        public synchronized Connection getConnection() throws SQLException {
133
            File f = H2SpatialUtils.getLocalFile(connectionParameters);
134
            boolean newdb = f!=null && !f.exists(); 
135
            
136
            Connection conn;
137
            
138
            try {
139
                conn = DriverManager.getConnection(
140
                    connectionParameters.getUrl(), 
141
                    connectionParameters.getUser(), 
142
                    connectionParameters.getPassword()
143
                );
144

    
145
            } catch(Throwable th) {
146
                throw th;
147
            }
148
            
149
            H2SpatialUtils.server_start(
150
                    this.connectionParameters.getServerPortAsString(), 
151
                    this.connectionParameters.getServerAllowOthers()
152
            );
153
            
154
//            if (this.dataSource == null) {
155
//                this.dataSource = this.createDataSource();               
156
//            }
157
//            
158
//            try {
159
//                conn = this.dataSource.getConnection();
160
//            } catch(Throwable th) {
161
//                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'. "+this.getStatus());
162
//                LOGGER.warn("Can't create connection to '"+this.dataSource.getUrl()+"'.",th);
163
//                throw th;
164
//            }
165
            
166
            
167
            try {
168
                conn.createStatement().execute("SELECT TOP 1 SRID FROM SPATIAL_REF_SYS");
169
            } catch(SQLException ex) {
170
                H2GISFunctions.load(conn);
171
            }
172

    
173
            String[] sqls = new String[] {
174
                "CREATE SCHEMA IF NOT EXISTS PUBLIC;SET SCHEMA PUBLIC",
175
                        Json_value.getSQL(),
176
                        Reverse.getSQL(),
177
                        Reverseinstr.getSQL()
178
            };
179
            for (String sql : sqls) {
180
                try {
181
                    conn.createStatement().execute(sql);
182
                } catch(SQLException ex) {
183
                    LOGGER.debug("Can't configure gvsig tables.",ex);
184
                    LOGGER.warn("Can't configure gvsig tables. "+sql);
185
                    // Ignore this error.
186
                }
187
            }
188
            
189
            if( newdb ) {
190
                    String[] sqls2 = new String[] {
191
                        "CREATE CACHED TABLE PUBLIC.\""+TABLE_RESOURCES_NAME+"\"(\""+FIELD_RESOURCES_NAME+"\" VARCHAR(150) NOT NULL, \""+FIELD_RESOURCES_RESOURCE+"\" BLOB DEFAULT NULL)",
192
                        "ALTER TABLE PUBLIC.\""+TABLE_RESOURCES_NAME+"\" ADD CONSTRAINT PUBLIC.CONSTRAINT_E PRIMARY KEY(\""+FIELD_RESOURCES_NAME+"\")",
193
                        "CREATE CACHED TABLE PUBLIC.\""+TABLE_CONFIGURATION_NAME+"\"(\""+FIELD_CONFIGURATION_NAME+"\" VARCHAR(200) NOT NULL, \""+FIELD_CONFIGURATION_VALUE+"\" CLOB DEFAULT NULL)",
194
                        "ALTER TABLE PUBLIC.\""+TABLE_CONFIGURATION_NAME+"\" ADD CONSTRAINT PUBLIC.CONSTRAINT_2 PRIMARY KEY(\""+FIELD_CONFIGURATION_NAME+"\")",
195
                    };
196
                    for (String sql : sqls2) {
197
                        try {
198
                            conn.createStatement().execute(sql);
199
                        } catch(SQLException ex) {
200
                            LOGGER.debug("Can't configure gvsig tables.",ex);
201
                            LOGGER.warn("Can't configure gvsig tables. "+sql);
202
                            // Ignore this error.
203
                        }
204
                    }
205
            }
206
            return conn;
207
        }
208
        
209
        private BasicDataSource createDataSource() throws SQLException {
210
            if (!this.isRegistered()) {
211
                this.registerDriver();
212
            }
213
            H2SpatialConnectionParameters params = connectionParameters;
214

    
215
            BasicDataSource ds = new BasicDataSource();
216
            ds.setDriverClassName(params.getJDBCDriverClassName());
217
            if( !StringUtils.isEmpty(params.getUser()) ) {
218
                ds.setUsername(params.getUser());
219
            }
220
            if( !StringUtils.isEmpty(params.getPassword()) ) {
221
                ds.setPassword(params.getPassword());
222
            }
223
            ds.setUrl(params.getUrl());
224

    
225
            ds.setMaxWait(60L * 1000);
226
            
227
            //
228
            // Ajustamos el pool para que las conexiones se cierren a los
229
            // 10 segundos, asi tratamos de que al salir de gvSIG no queden
230
            // conexiones abiertas con la BBDD y pueda quedar corrupta esta.
231
            // Hay que tener en cuenta que es una BBDD embebida, y mientras
232
            // hayan conexiones abiertas pueden quedar cosas por bajar a disco.
233
            //
234
            int sidle = this.connectionParameters.getMaxSecondsIdle();
235
            if( sidle < 0 ) {
236
                ds.setTimeBetweenEvictionRunsMillis(-1);
237
                ds.setMinEvictableIdleTimeMillis(30*1000);
238
            } else {
239
                // Revisamos las conexiones inactivas cada 10 segundos
240
                ds.setTimeBetweenEvictionRunsMillis(sidle*1000);
241
                // Eliminadmos las conexiones que lleven inactivas mas de 10 segundos.
242
                ds.setMinEvictableIdleTimeMillis(sidle*1000);
243
            }
244
            
245
            // Ajustamos el numero minimo de conexiones a 0 para permitir
246
            // que se lleguen a cerrar todas las conexiones del pool.
247
            ds.setMinIdle(0);
248
            // dejaremos el MaxIdle a 20, no parece importante. .
249
            ds.setMaxIdle(20);
250
            
251
            return ds;
252
        }
253

    
254
        private boolean isRegistered() {
255
            return needRegisterDriver;
256
        }
257

    
258
        @Override
259
        public void registerDriver() throws SQLException {
260
            String className = this.connectionParameters.getJDBCDriverClassName();
261
            if (className == null) {
262
                return;
263
            }
264
            try {
265
                Class theClass = Class.forName(className);
266
                if (theClass == null) {
267
                    throw new JDBCDriverClassNotFoundException(H2SpatialLibrary.NAME, className);
268
                }
269
            } catch (Exception e) {
270
                throw new SQLException("Can't register JDBC driver '" + className + "'.", e);
271
            }
272
            needRegisterDriver = false;
273
        }
274

    
275
        @Override
276
        public void dispose() {
277
            closeDataSource();
278
            this.connectionParameters = null;
279
        }
280
        
281
        private void closeDataSource() {
282
            try {
283
                if( dataSource!=null ) {
284
                    LOGGER.info("Clossing connection pool.");
285
                    LOGGER.info(this.getStatus());
286
                    dataSource.close();
287
                    dataSource = null;
288
                    LOGGER.info("Connection pool closed.");
289
                    LOGGER.info(this.getStatus());
290
                }
291
            } catch (Throwable th) {
292
                LOGGER.warn("Problems closing connections pool.", th);
293
            }
294

    
295
        }
296

    
297
        @Override
298
        public boolean isDisposed() {
299
            return this.connectionParameters == null;
300
        }
301
    }
302

    
303
    private ConnectionProvider connectionProvider = null;
304
 
305
    /**
306
     * Constructor for use only for testing purposes.
307
     * 
308
     * @param connectionParameters
309
     * @param connectionProvider
310
     */
311
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters, ConnectionProvider connectionProvider) { 
312
        super(connectionParameters);
313
        this.srssolver = new SRSSolverDumb(this);
314
        this.connectionProvider = connectionProvider;
315
    }
316
  
317
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters) {
318
        super(connectionParameters);
319
//        this.srssolver = new SRSSolverBase(this);
320
        this.srssolver = new SRSSolverDumb(this); //Tratando de reducir accesos a la BBDD ? Ponerlo en los parametros ?
321
    }
322

    
323
    public void  shutdown() {
324
        try {
325
            try {
326
                this.getConnection();
327
            } catch(Throwable th) {
328
                LOGGER.debug("Can't get connection-provider",th);
329
            }
330
            if( this.connectionProvider!=null ) {
331
                ((ConnectionProviderImpl) this.connectionProvider).shutdown();
332
                this.connectionProvider = null;
333
            }
334
            H2SpatialUtils.server_stop();
335
        } catch (Throwable ex) {
336
            LOGGER.warn("Problems shutdown H2", ex);
337
        }
338
    }
339

    
340
    private void logConnectionStatus(String msg, Connection conn) {
341
        ConnectionProvider cp = this.getConnectionProvider();
342
        StringBuilder builder = new StringBuilder();
343
        builder.append(msg);
344
        if( conn == null ) {
345
            builder.append(": connection null");
346
        } else {
347
            Boolean closed = null;
348
            try {
349
                closed = conn.isClosed();
350
            } catch(Throwable th) {
351
            }
352
            builder.append(": connection ");
353
            builder.append(JDBCUtils.getConnId(conn));
354
            if( closed ) {
355
                builder.append(" (c)");
356
            }
357
            builder.append(" ");
358
        }
359
        builder.append(cp.getStatus());
360
        LOGGER.info(builder.toString());
361
    }
362
        
363
    public ConnectionProvider getConnectionProvider() {
364
        if (this.connectionProvider == null) {
365
          H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
366
          if( connectionParameters==null ) {
367
            return null; // Testing mode?
368
          }
369
          this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
370
        }
371
        return this.connectionProvider;
372
    }
373
    
374
    @Override
375
    public synchronized JDBCConnection  getConnection() throws AccessResourceException {
376
        try {
377
            H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
378
            JDBCConnection conn = (JDBCConnection) DataTransactionServices.getConnection(
379
                    transaction, 
380
                    this.getConnectionProviderKey(connectionParameters)
381
            );
382
            if( conn != null ) {
383
                return conn;
384
            }
385
            if (this.connectionProvider == null) {
386
              if( connectionParameters==null ) {
387
                return null; // Testing mode?
388
              }
389
              this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
390
            }
391
            JDBCConnection connection = new JDBCConnectionBase(
392
                    this.transaction, 
393
                    this.connectionProvider.getConnection(), 
394
                    this.getConnectionProviderKey(connectionParameters)
395
            );
396
            if( LOGGER.isDebugEnabled() ) {
397
                LOGGER.debug("["+JDBCUtils.getConnId(connection.get())+"] getConnection "+connectionProvider.getStatus()+" "+ connectionProvider.toString());
398
            }
399
            return connection;
400
        } catch (SQLException ex) {
401
            throw new AccessResourceException(H2SpatialLibrary.NAME, ex);
402
        }
403
    }
404

    
405
    @Override
406
    public H2SpatialConnectionParameters getConnectionParameters() {
407
        return (H2SpatialConnectionParameters) super.getConnectionParameters();
408
    }
409
    
410
    @Override
411
    public String getConnectionURL() {
412
        return H2SpatialUtils.getConnectionURL(this.getConnectionParameters());
413
    }
414

    
415
    @Override
416
    protected String getResourceType() {
417
        return H2SpatialLibrary.NAME;
418
    }
419

    
420
    @Override
421
    public String getProviderName() {
422
        return H2SpatialLibrary.NAME;
423
    }
424

    
425
    @Override
426
    public JDBCSQLBuilderBase createSQLBuilder() {
427
        return new H2SpatialSQLBuilder(this);
428
    }
429
    
430
    @Override
431
    public OperationsFactory getOperations() {
432
        if (this.operationsFactory == null) {
433
            this.operationsFactory = new H2SpatialOperationsFactory(this);
434
        }
435
        return operationsFactory;
436
    }
437

    
438
    @Override
439
    public GeometrySupportType getGeometrySupportType() {
440
        return GeometrySupportType.WKB;
441
    }
442

    
443
    @Override
444
    public boolean hasSpatialFunctions() {
445
        return true;
446
    }
447

    
448
    @Override
449
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
450
        return true;
451
    }
452

    
453
    @Override
454
    public String getQuoteForIdentifiers() {
455
        return "\"";
456
    }
457

    
458
    @Override
459
    public boolean allowAutomaticValues() {
460
        return true;
461
    }
462

    
463
    @Override
464
    public boolean supportOffsetInSelect() {
465
        return true;
466
    }
467

    
468
    @Override
469
    public String getQuoteForStrings() {
470
        return "'";
471
    }
472

    
473
    @Override
474
    public String getSourceId(JDBCStoreParameters parameters) {
475
        H2SpatialStoreParameters params = (H2SpatialStoreParameters) parameters;
476
        StringBuilder builder = new StringBuilder();
477
        builder.append(params.getTable());
478
        builder.append("(");
479
        boolean needComma = false;
480
        if( StringUtils.isNotBlank(params.getHost()) ) {
481
            builder.append("host=");
482
            builder.append(params.getHost());
483
            needComma = true;
484
        }
485
        if( params.getPort()>0 ) {
486
            if (needComma ) {
487
                builder.append(", ");
488
            }
489
            builder.append("port=");
490
            builder.append(params.getPort());
491
            needComma = true;
492
        }
493
        if( StringUtils.isNotBlank(params.getDBName()) ) {
494
            if (needComma ) {
495
                builder.append(", ");
496
            }
497
            builder.append("db=");
498
            builder.append(params.getDBName());
499
            needComma = true;
500
        }
501
        if( StringUtils.isNotBlank(params.getSchema()) ) {
502
            if (needComma ) {
503
                builder.append(", ");
504
            }
505
            builder.append("schema=");
506
            builder.append(params.getSchema());
507
            needComma = true;
508
        }
509
        File f = params.getFile();       
510
        if( f != null) {
511
            if (needComma ) {
512
                builder.append(", ");
513
            }
514
            builder.append("file=");
515
            builder.append(params.getFile().getAbsolutePath());
516
        }
517
        builder.append(")");
518
        return builder.toString();
519

    
520
    }
521
    
522
    
523
    
524

    
525
    @Override
526
    public JDBCNewStoreParameters createNewStoreParameters() {
527
        return new H2SpatialNewStoreParameters();
528
    }
529

    
530
    @Override
531
    public JDBCStoreParameters createOpenStoreParameters() {
532
        return new H2SpatialStoreParameters();
533
    }
534

    
535
    @Override
536
    public JDBCServerExplorerParameters createServerExplorerParameters() {
537
        return new H2SpatialExplorerParameters();
538
    }
539

    
540
    @Override
541
    public JDBCServerExplorer createServerExplorer(
542
            JDBCServerExplorerParameters parameters, 
543
            DataServerExplorerProviderServices providerServices
544
        ) throws InitializeException {
545
        
546
        JDBCServerExplorer explorer = new H2SpatialExplorer(
547
                parameters, 
548
                providerServices, 
549
                this
550
        );
551
        this.initialize(explorer, parameters, null);
552
        return explorer;
553
    }
554
    
555
    public String getConnectionProviderStatus(){
556
        return this.getConnectionProvider().getStatus();
557
    }
558

    
559
    @Override
560
    public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
561
        JDBCStoreParameters p = super.createOpenStoreParameters(parameters); 
562
        if(StringUtils.isBlank(p.getCatalog())){
563
            p.setCatalog(StringUtils.upperCase(p.getDBName()));
564
        }
565
        return p;
566
    }
567
    
568
    
569
}