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

History | View | Annotate | Download (20 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(transaction, connectionParameters.getUrl());
379
            if( conn != null ) {
380
                return conn;
381
            }
382
            if (this.connectionProvider == null) {
383
              if( connectionParameters==null ) {
384
                return null; // Testing mode?
385
              }
386
              this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
387
            }
388
            JDBCConnection connection = new JDBCConnectionBase(
389
                    this.transaction, 
390
                    this.connectionProvider.getConnection(), 
391
                    connectionParameters.getUrl()
392
            );
393
            if( LOGGER.isDebugEnabled() ) {
394
                LOGGER.debug("["+JDBCUtils.getConnId(connection.get())+"] getConnection "+connectionProvider.getStatus()+" "+ connectionProvider.toString());
395
            }
396
            return connection;
397
        } catch (SQLException ex) {
398
            throw new AccessResourceException(H2SpatialLibrary.NAME, ex);
399
        }
400
    }
401

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

    
412
    @Override
413
    protected String getResourceType() {
414
        return H2SpatialLibrary.NAME;
415
    }
416

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

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

    
435
    @Override
436
    public GeometrySupportType getGeometrySupportType() {
437
        return GeometrySupportType.WKB;
438
    }
439

    
440
    @Override
441
    public boolean hasSpatialFunctions() {
442
        return true;
443
    }
444

    
445
    @Override
446
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
447
        return true;
448
    }
449

    
450
    @Override
451
    public String getQuoteForIdentifiers() {
452
        return "\"";
453
    }
454

    
455
    @Override
456
    public boolean allowAutomaticValues() {
457
        return true;
458
    }
459

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

    
465
    @Override
466
    public String getQuoteForStrings() {
467
        return "'";
468
    }
469

    
470
    @Override
471
    public String getSourceId(JDBCStoreParameters parameters) {
472
        H2SpatialStoreParameters h2params = (H2SpatialStoreParameters) parameters;
473
        StringBuilder builder = new StringBuilder();
474
        builder.append(h2params.getTable());
475
        builder.append("(");
476
        if( StringUtils.isNotBlank(h2params.getHost()) ) {
477
            builder.append(h2params.getHost());
478
        }
479
        if( h2params.getPort()>0 ) {
480
            builder.append(",");
481
            builder.append(h2params.getPort());
482
        }
483
        File f = h2params.getFile();       
484
        if( f != null ) {
485
            builder.append(",");
486
            builder.append(h2params.getFile().getAbsolutePath());
487
        }
488
        builder.append(")");
489
        return builder.toString();
490
    }
491

    
492
    @Override
493
    public JDBCNewStoreParameters createNewStoreParameters() {
494
        return new H2SpatialNewStoreParameters();
495
    }
496

    
497
    @Override
498
    public JDBCStoreParameters createOpenStoreParameters() {
499
        return new H2SpatialStoreParameters();
500
    }
501

    
502
    @Override
503
    public JDBCServerExplorerParameters createServerExplorerParameters() {
504
        return new H2SpatialExplorerParameters();
505
    }
506

    
507
    @Override
508
    public JDBCServerExplorer createServerExplorer(
509
            JDBCServerExplorerParameters parameters, 
510
            DataServerExplorerProviderServices providerServices
511
        ) throws InitializeException {
512
        
513
        JDBCServerExplorer explorer = new H2SpatialExplorer(
514
                parameters, 
515
                providerServices, 
516
                this
517
        );
518
        this.initialize(explorer, parameters, null);
519
        return explorer;
520
    }
521
    
522
    public String getConnectionProviderStatus(){
523
        return this.getConnectionProvider().getStatus();
524
    }
525

    
526
    @Override
527
    public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
528
        JDBCStoreParameters p = super.createOpenStoreParameters(parameters); 
529
        if(StringUtils.isBlank(p.getCatalog())){
530
            p.setCatalog(StringUtils.upperCase(p.getDBName()));
531
        }
532
        return p;
533
    }
534
    
535
    
536
}