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

History | View | Annotate | Download (21.2 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 H2SpatialConnectionProvider 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 H2SpatialConnectionProvider(H2SpatialConnectionParameters connectionParameters) {
83
            this.connectionParameters = connectionParameters;
84
        }
85

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

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

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

    
220
            BasicDataSource ds = new BasicDataSource();
221
            ds.setDriverClassName(params.getJDBCDriverClassName());
222
            if( !StringUtils.isEmpty(params.getUser()) ) {
223
                ds.setUsername(params.getUser());
224
            }
225
            if( !StringUtils.isEmpty(params.getPassword()) ) {
226
                ds.setPassword(params.getPassword());
227
            }
228
            ds.setUrl(params.getUrl());
229

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

    
259
        private boolean isRegistered() {
260
            return needRegisterDriver;
261
        }
262

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

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

    
300
        }
301

    
302
        @Override
303
        public boolean isDisposed() {
304
            return this.connectionParameters == null;
305
        }
306
    }
307

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

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

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

    
412
    @Override
413
    public H2SpatialConnectionParameters getConnectionParameters() {
414
        return (H2SpatialConnectionParameters) super.getConnectionParameters();
415
    }
416
    
417
    @Override
418
    public String getConnectionURL() {
419
        return H2SpatialUtils.getConnectionURL(this.getConnectionParameters());
420
    }
421

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

    
427
    @Override
428
    public String getProviderName() {
429
        return H2SpatialLibrary.NAME;
430
    }
431

    
432
    @Override
433
    public JDBCSQLBuilderBase createSQLBuilder() {
434
        return new H2SpatialSQLBuilder(this);
435
    }
436
    
437
    @Override
438
    public OperationsFactory getOperations() {
439
        if (this.operationsFactory == null) {
440
            this.operationsFactory = new H2SpatialOperationsFactory(this);
441
        }
442
        return operationsFactory;
443
    }
444

    
445
    @Override
446
    public GeometrySupportType getGeometrySupportType() {
447
        return GeometrySupportType.WKB;
448
    }
449

    
450
    @Override
451
    public boolean hasSpatialFunctions() {
452
        return true;
453
    }
454

    
455
    @Override
456
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
457
        return true;
458
    }
459

    
460
    @Override
461
    public String getQuoteForIdentifiers() {
462
        return "\"";
463
    }
464

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

    
470
    @Override
471
    public boolean supportOffsetInSelect() {
472
        return true;
473
    }
474

    
475
    @Override
476
    public String getQuoteForStrings() {
477
        return "'";
478
    }
479

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

    
527
    }
528
    
529
    
530
    
531

    
532
    @Override
533
    public JDBCNewStoreParameters createNewStoreParameters() {
534
        return new H2SpatialNewStoreParameters();
535
    }
536

    
537
    @Override
538
    public JDBCStoreParameters createOpenStoreParameters() {
539
        return new H2SpatialStoreParameters();
540
    }
541

    
542
    @Override
543
    public JDBCServerExplorerParameters createServerExplorerParameters() {
544
        return new H2SpatialExplorerParameters();
545
    }
546

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

    
567
    @Override
568
    public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
569
        JDBCStoreParameters p = super.createOpenStoreParameters(parameters); 
570
        if(StringUtils.isBlank(p.getCatalog())){
571
            p.setCatalog(StringUtils.upperCase(p.getDBName()));
572
        }
573
        return p;
574
    }
575
}