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 @ 47456

History | View | Annotate | Download (21 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
    @SuppressWarnings("FieldNameHidesFieldInSuperclass")
70
    protected static final Logger LOGGER = LoggerFactory.getLogger(H2SpatialHelper.class);
71

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

    
76
        private static boolean needRegisterDriver = true;
77

    
78
        private BasicDataSource dataSource = null;
79

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

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

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

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

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

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

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

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

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

    
296
        }
297

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

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

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

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

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

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

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

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

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

    
445
    @Override
446
    public boolean hasSpatialFunctions() {
447
        return true;
448
    }
449

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

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

    
460
    @Override
461
    public boolean allowAutomaticValues() {
462
        return true;
463
    }
464

    
465
    @Override
466
    public boolean supportOffsetInSelect() {
467
        return true;
468
    }
469

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

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

    
522
    }
523
    
524
    
525
    
526

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

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

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

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

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