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

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

    
63

    
64
@SuppressWarnings("UseSpecificCatch")
65
public class H2SpatialHelper extends JDBCHelperBase {
66

    
67
    static final Logger LOGGER = LoggerFactory.getLogger(H2SpatialHelper.class);
68

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

    
132
    public static class ConnectionProviderImpl implements ConnectionProvider {
133

    
134
        private static boolean needRegisterDriver = true;
135

    
136
        private BasicDataSource dataSource = null;
137

    
138
        private H2SpatialConnectionParameters connectionParameters;
139
        
140
        private static Server server = null;
141
        private static boolean startServer = true;
142

    
143
        public ConnectionProviderImpl(H2SpatialConnectionParameters connectionParameters) {
144
            this.connectionParameters = connectionParameters;
145
        }
146

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

    
249
        }
250

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

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

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

    
354
        private boolean isRegistered() {
355
            return needRegisterDriver;
356
        }
357

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

    
375
        @Override
376
        public void dispose() {
377
            if( this.dataSource!=null ) {
378
                try {
379
                    this.dataSource.close();
380
                } catch (SQLException ex) {
381
                    LOGGER.warn("Can't close BasicDataSource", ex);
382
                }
383
                this.dataSource = null;
384
            }
385
            this.connectionParameters = null;
386
        }
387

    
388
    }
389

    
390
    private ConnectionProvider connectionProvider = null;
391
 
392
    /**
393
     * Constructor for use only for testing purposes.
394
     * 
395
     * @param connectionParameters
396
     * @param connectionProvider
397
     */
398
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters, ConnectionProvider connectionProvider) { 
399
        super(connectionParameters);
400
        this.srssolver = new SRSSolverDumb(this);
401
        this.connectionProvider = connectionProvider;
402
    }
403
  
404
    public H2SpatialHelper(JDBCConnectionParameters connectionParameters) {
405
        super(connectionParameters);
406
        this.srssolver = new SRSSolverBase(this);
407
    }
408

    
409
    
410
    public void  shutdown() {
411
        try {
412
            if( this.connectionProvider!=null ) {
413
                ((ConnectionProviderImpl) this.connectionProvider).shutdown();
414
                this.connectionProvider = null;
415
            }
416
            ConnectionProviderImpl.stopServer();
417
        } catch (Throwable ex) {
418
            LOGGER.warn("Problems shutdown H2", ex);
419
        }
420
    }
421

    
422
    private void logConnectionStatus(String msg, Connection conn) {
423
        ConnectionProvider cp = this.getConnectionProvider();
424
        StringBuilder builder = new StringBuilder();
425
        builder.append(msg);
426
        if( conn == null ) {
427
            builder.append(": connection null");
428
        } else {
429
            Boolean closed = null;
430
            try {
431
                closed = conn.isClosed();
432
            } catch(Throwable th) {
433
            }
434
            builder.append(": connection ");
435
            builder.append(JDBCUtils.getConnId(conn));
436
            if( closed ) {
437
                builder.append(" (c)");
438
            }
439
            builder.append(" ");
440
        }
441
        builder.append(cp.getStatus());
442
        LOGGER.info(builder.toString());
443
    }
444
        
445
    private ConnectionProvider getConnectionProvider() {
446
        if (this.connectionProvider == null) {
447
          H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
448
          if( connectionParameters==null ) {
449
            return null; // Testing mode?
450
          }
451
          this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
452
        }
453
        return this.connectionProvider;
454
    }
455
    
456
    @Override
457
    public synchronized Connection  getConnection() throws AccessResourceException {
458
        try {
459
            if (this.connectionProvider == null) {
460
              H2SpatialConnectionParameters connectionParameters = this.getConnectionParameters();
461
              if( connectionParameters==null ) {
462
                return null; // Testing mode?
463
              }
464
              this.connectionProvider = new ConnectionProviderImpl(connectionParameters);
465
            }
466
            Connection connection = this.connectionProvider.getConnection();
467
            if( LOGGER.isDebugEnabled() ) {
468
                LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] getConnection "+connectionProvider.getStatus()+" "+ connectionProvider.toString());
469
            }
470
            return connection;
471
        } catch (SQLException ex) {
472
            throw new AccessResourceException(H2SpatialLibrary.NAME, ex);
473
        }
474
    }
475

    
476
    @Override
477
    public void closeConnection(Connection connection) {
478
      if( connection!=null ) { // In test ???
479
        LOGGER.debug("["+JDBCUtils.getConnId(connection)+"] closeConnection "+ this.connectionProvider.getStatus());
480
      }
481
      super.closeConnection(connection);
482
    }
483
    
484
    @Override
485
    public H2SpatialConnectionParameters getConnectionParameters() {
486
        return (H2SpatialConnectionParameters) super.getConnectionParameters();
487
    }
488
    
489
    @Override
490
    public String getConnectionURL() {
491
        return getConnectionURL(this.getConnectionParameters());
492
    }
493

    
494
    @Override
495
    protected String getResourceType() {
496
        return H2SpatialLibrary.NAME;
497
    }
498

    
499
    @Override
500
    public String getProviderName() {
501
        return H2SpatialLibrary.NAME;
502
    }
503

    
504
    @Override
505
    public JDBCSQLBuilderBase createSQLBuilder() {
506
        return new H2SpatialSQLBuilder(this);
507
    }
508
    
509
    @Override
510
    public OperationsFactory getOperations() {
511
        if (this.operationsFactory == null) {
512
            this.operationsFactory = new H2SpatialOperationsFactory(this);
513
        }
514
        return operationsFactory;
515
    }
516

    
517
    @Override
518
    public GeometrySupportType getGeometrySupportType() {
519
        return GeometrySupportType.WKB;
520
    }
521

    
522
    @Override
523
    public boolean hasSpatialFunctions() {
524
        return true;
525
    }
526

    
527
    @Override
528
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
529
        return true;
530
    }
531

    
532
    @Override
533
    public String getQuoteForIdentifiers() {
534
        return "\"";
535
    }
536

    
537
    @Override
538
    public boolean allowAutomaticValues() {
539
        return true;
540
    }
541

    
542
    @Override
543
    public boolean supportOffsetInSelect() {
544
        return true;
545
    }
546

    
547
    @Override
548
    public String getQuoteForStrings() {
549
        return "'";
550
    }
551

    
552
    @Override
553
    public String getSourceId(JDBCStoreParameters parameters) {
554
        H2SpatialStoreParameters h2params = (H2SpatialStoreParameters) parameters;
555
        StringBuilder builder = new StringBuilder();
556
        builder.append(h2params.getTable());
557
        builder.append("(");
558
        if( StringUtils.isNotBlank(h2params.getHost()) ) {
559
            builder.append(h2params.getHost());
560
        }
561
        if( h2params.getPort()>0 ) {
562
            builder.append(",");
563
            builder.append(h2params.getPort());
564
        }
565
        File f = h2params.getFile();       
566
        if( f != null ) {
567
            builder.append(",");
568
            builder.append(h2params.getFile().getAbsolutePath());
569
        }
570
        builder.append(")");
571
        return builder.toString();
572
    }
573

    
574
    @Override
575
    public JDBCNewStoreParameters createNewStoreParameters() {
576
        return new H2SpatialNewStoreParameters();
577
    }
578

    
579
    @Override
580
    public JDBCStoreParameters createOpenStoreParameters() {
581
        return new H2SpatialStoreParameters();
582
    }
583

    
584
    @Override
585
    public JDBCServerExplorerParameters createServerExplorerParameters() {
586
        return new H2SpatialExplorerParameters();
587
    }
588

    
589
    @Override
590
    public JDBCServerExplorer createServerExplorer(
591
            JDBCServerExplorerParameters parameters, 
592
            DataServerExplorerProviderServices providerServices
593
        ) throws InitializeException {
594
        
595
        JDBCServerExplorer explorer = new H2SpatialExplorer(
596
                parameters, 
597
                providerServices, 
598
                this
599
        );
600
        this.initialize(explorer, parameters, null);
601
        return explorer;
602
    }
603
    
604
}