Statistics
| Revision:

gvsig-sqlite / trunk / org.gvsig.spatialite / org.gvsig.spatialite.provider / src / main / java / org / gvsig / spatialite / dal / SpatiaLiteHelper.java @ 194

History | View | Annotate | Download (13.8 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2016 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.spatialite.dal;
23

    
24
import java.io.File;
25
import org.gvsig.spatialite.dal.operations.SpatiaLiteOperationsFactory;
26
import java.sql.Connection;
27
import java.sql.SQLException;
28
import java.sql.Statement;
29
import java.util.Map;
30
import org.apache.commons.dbcp.BasicDataSource;
31
import org.apache.commons.io.FilenameUtils;
32
import org.apache.commons.lang3.StringUtils;
33
import org.gvsig.fmap.dal.SQLBuilder;
34
import org.gvsig.fmap.dal.exception.InitializeException;
35
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
36
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
37
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
38
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
39
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
40
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
41
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
42
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException;
43
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
44
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
45
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
46
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
47
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase;
48
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
49
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverBase;
50
import org.gvsig.fmap.geom.Geometry;
51
import org.gvsig.fmap.geom.aggregate.MultiLine;
52
import org.gvsig.fmap.geom.aggregate.MultiPoint;
53
import org.gvsig.fmap.geom.aggregate.MultiPolygon;
54
import org.gvsig.fmap.geom.exception.CreateGeometryException;
55
import org.gvsig.fmap.geom.primitive.Primitive;
56
import org.gvsig.fmap.geom.type.GeometryType;
57
import org.slf4j.Logger;
58
import org.slf4j.LoggerFactory;
59
import org.sqlite.SQLiteConfig;
60
import org.sqlite.SQLiteConfig.TransactionMode;
61

    
62
public class SpatiaLiteHelper extends JDBCHelperBase {
63

    
64
    static final Logger logger = LoggerFactory.getLogger(SpatiaLiteHelper.class);
65

    
66
    public static final String NAME = "SpatiaLite";
67
    public static final String SpatiaLiteJDBCDriver = "org.sqlite.JDBC";
68
    public static final String URL_FORMAT = "jdbc:sqlite:%s";
69
    
70
    public static String getConnectionURL(SpatiaLiteConnectionParameters params) {
71
        String fname = params.getFile().getAbsolutePath().replace("\\","/");
72
        if( StringUtils.isEmpty(FilenameUtils.getExtension(fname)) ) {
73
            fname = fname + ".sqlite";
74
            params.setFile(new File(fname));
75
        }
76
        String driverClassName = params.getJDBCDriverClassName();
77
        if( StringUtils.isNotBlank(driverClassName) && 
78
            !StringUtils.equals(driverClassName, SpatiaLiteJDBCDriver) ) {
79
            try {
80
                Class.forName(driverClassName);
81
            } catch(Throwable th) {
82
                logger.warn("Can't load SpatiaLite JDBC Driver '"+driverClassName+"'.",th);
83
            }            
84
        }
85
        String connectionURLFormat = params.getURLFormat(); 
86
        if( StringUtils.isBlank(connectionURLFormat) ) {
87
            connectionURLFormat = URL_FORMAT;
88
        }
89
        String connectionURL = String.format(connectionURLFormat, fname);
90
        logger.debug("connectionURL: {}", connectionURL);
91
        return connectionURL;
92
    }
93

    
94
    private static class ConnectionProvider {
95

    
96
        private static boolean needRegisterDriver = true;
97

    
98
        private BasicDataSource dataSource = null;
99

    
100
        private final SpatiaLiteConnectionParameters connectionParameters;
101

    
102
        public ConnectionProvider(SpatiaLiteConnectionParameters connectionParameters) {
103
            this.connectionParameters = connectionParameters;
104
        }
105

    
106
        public Connection getConnection() throws SQLException {
107
            boolean newDs = (this.dataSource == null);
108
            if (newDs) {
109
                this.dataSource = this.createDataSource();               
110
            }
111
            Connection conn = this.dataSource.getConnection();
112
            loadExtension(conn);
113
            if (newDs) {
114
                    initSpatialMetadata(conn);
115
            }
116
            return conn;
117
        }
118

    
119
        private BasicDataSource createDataSource() throws SQLException {
120
            if (!this.isRegistered()) {
121
                this.registerDriver();
122
            }
123
            SpatiaLiteConnectionParameters params = connectionParameters;
124

    
125
            BasicDataSource ds = new BasicDataSource();
126
            ds.setDriverClassName(params.getJDBCDriverClassName());
127
            if( !StringUtils.isEmpty(params.getUser()) ) {
128
                ds.setUsername(params.getUser());
129
            }
130
            if( !StringUtils.isEmpty(params.getPassword()) ) {
131
                ds.setPassword(params.getPassword());
132
            }
133
            ds.setUrl(params.getUrl());
134
            SQLiteConfig config = new SQLiteConfig();
135
            config.setSharedCache(true);
136
            config.enableLoadExtension(true);
137
            config.setTransactionMode(TransactionMode.IMMEDIATE);
138
            for (Map.Entry e : config.toProperties().entrySet()) {
139
                ds.addConnectionProperty((String)e.getKey(), (String)e.getValue());
140
            }
141
            ds.setMaxWait(60L * 1000);
142
            return ds;
143
        }
144

    
145
        private boolean isRegistered() {
146
            return needRegisterDriver;
147
        }
148

    
149
        public void registerDriver() throws SQLException {
150
            String className = this.connectionParameters.getJDBCDriverClassName();
151
            if (className == null) {
152
                return;
153
            }
154
            try {
155
                Class theClass = Class.forName(className);
156
                if (theClass == null) {
157
                    throw new JDBCDriverClassNotFoundException(NAME, className);
158
                }
159
            } catch (Exception e) {
160
                throw new SQLException("Can't register JDBC driver '" + className + "'.", e);
161
            }
162
            needRegisterDriver = false;
163
        }
164
        
165
        public void loadExtension(Connection conn) {
166
                    Statement st = null;
167
            try {
168
                st = conn.createStatement();
169
                JDBCUtils.execute(st, "SELECT load_extension('mod_spatialite')");
170
            } catch(Exception ex) {
171
                logger.warn("Can't load mod_spatialite extension module for SQLite (" +
172
                        " driver class "+StringUtils.defaultString(connectionParameters.getJDBCDriverClassName()) +
173
                        " url format "+StringUtils.defaultString(connectionParameters.getURLFormat()) +
174
                        " file "+connectionParameters.getFile()==null?"null":connectionParameters.getFile().getAbsolutePath() +
175
                        ").", ex);
176
            }
177
            finally {
178
                    JDBCUtils.closeQuietly(st);
179
            }          
180
        }
181
        
182
        public void initSpatialMetadata(Connection conn) {
183
                Statement st = null;
184
                try {
185
                        st = conn.createStatement();
186
                        //harmless if already existing
187
                        JDBCUtils.execute(st, "SELECT InitSpatialMetaData(1)");
188
                } catch(Exception ex) {
189
                logger.warn("Can't initialize spatial metatada in SQLite database (" +
190
                        " driver class "+StringUtils.defaultString(connectionParameters.getJDBCDriverClassName()) +
191
                        " url format "+StringUtils.defaultString(connectionParameters.getURLFormat()) +
192
                        " file "+connectionParameters.getFile()==null?"null":connectionParameters.getFile().getAbsolutePath() +
193
                        ").", ex);
194
                }
195
                finally {
196
                        JDBCUtils.closeQuietly(st);
197
                }
198
        }
199

    
200
    }
201

    
202
    private ConnectionProvider connectionProvider = null;
203
   
204
    public SpatiaLiteHelper(JDBCConnectionParameters connectionParameters) {
205
        super(connectionParameters);
206
        this.srssolver = new SRSSolverBase(this);
207
    }
208

    
209
    @Override
210
    public synchronized Connection  getConnection() throws AccessResourceException {
211
        try {
212
            if (this.connectionProvider == null) {
213
                this.connectionProvider = new ConnectionProvider(this.getConnectionParameters());
214
            }
215
            Connection connection = this.connectionProvider.getConnection();
216
            logger.debug("getConnection: connection = "+connection.hashCode());
217
            return connection;
218
        } catch (SQLException ex) {
219
            throw new AccessResourceException(SpatiaLiteLibrary.NAME, ex);
220
        }
221
    }
222
    
223
    @Override
224
    public SpatiaLiteConnectionParameters getConnectionParameters() {
225
        return (SpatiaLiteConnectionParameters) super.getConnectionParameters();
226
    }
227
    
228
    @Override
229
    public String getConnectionURL() {
230
        return getConnectionURL(this.getConnectionParameters());
231
    }
232

    
233
    @Override
234
    protected String getResourceType() {
235
        return SpatiaLiteLibrary.NAME;
236
    }
237

    
238
    @Override
239
    public String getProviderName() {
240
        return SpatiaLiteLibrary.NAME;
241
    }
242

    
243
    @Override
244
    public JDBCSQLBuilderBase createSQLBuilder() {
245
        return new SpatiaLiteSQLBuilder(this);
246
    }
247
    
248
    @Override
249
    public OperationsFactory getOperations() {
250
        if (this.operationsFactory == null) {
251
            this.operationsFactory = new SpatiaLiteOperationsFactory(this);
252
        }
253
        return operationsFactory;
254
    }
255

    
256
    @Override
257
    public SQLBuilder.GeometrySupportType getGeometrySupportType() {
258
        return SQLBuilder.GeometrySupportType.WKB;
259
    }
260

    
261
    @Override
262
    public boolean hasSpatialFunctions() {
263
        return true;
264
    }
265

    
266
    @Override
267
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
268
        return true;
269
    }
270

    
271
    @Override
272
    public String getQuoteForIdentifiers() {
273
        return "\"";
274
    }
275

    
276
    @Override
277
    public boolean allowAutomaticValues() {
278
        return true;
279
    }
280

    
281
    @Override
282
    public boolean supportOffsetInSelect() {
283
        return true;
284
    }
285

    
286
    @Override
287
    public String getQuoteForStrings() {
288
        return "'";
289
    }
290

    
291
    @Override
292
    public String getSourceId(JDBCStoreParameters parameters) {
293
        return parameters.getDBName() + "." + 
294
               parameters.getSchema()+ "." + 
295
               parameters.getTable();
296
    }
297

    
298
    @Override
299
    public JDBCNewStoreParameters createNewStoreParameters() {
300
        return new SpatiaLiteNewStoreParameters();
301
    }
302

    
303
    @Override
304
    public JDBCStoreParameters createOpenStoreParameters() {
305
        return new SpatiaLiteStoreParameters();
306
    }
307

    
308
    @Override
309
    public JDBCServerExplorerParameters createServerExplorerParameters() {
310
        return new SpatiaLiteExplorerParameters();
311
    }
312

    
313
    public Geometry forceGeometryType(GeometryType geomtype, Geometry geom) throws CreateGeometryException {
314
        if( geom == null ) {
315
            return null;
316
        }
317
        switch( geomtype.getType() ) {
318
        case Geometry.TYPES.MULTIPOLYGON:
319
            if( geom.getType()==Geometry.TYPES.POLYGON ) {
320
                MultiPolygon x = getGeometryManager().createMultiPolygon(geomtype.getSubType());
321
                x.addPrimitive((Primitive) geom);
322
                geom = x;
323
            }
324
            break;
325
        case Geometry.TYPES.MULTILINE:
326
            if( geom.getType()==Geometry.TYPES.LINE ) {
327
                MultiLine x = getGeometryManager().createMultiLine(geomtype.getSubType());
328
                x.addPrimitive((Primitive) geom);
329
                geom = x;
330
            }
331
            break;
332
        case Geometry.TYPES.MULTIPOINT:
333
            if( geom.getType()==Geometry.TYPES.POINT ) {
334
                MultiLine x = getGeometryManager().createMultiLine(geomtype.getSubType());
335
                x.addPrimitive((Primitive) geom);
336
                geom = x;
337
            }
338
            break;
339
        case Geometry.TYPES.POLYGON:
340
            if( geom.getType()==Geometry.TYPES.MULTIPOLYGON ) {
341
                MultiPolygon x = (MultiPolygon) geom;
342
                if( x.getPrimitivesNumber()==1 ) {
343
                    geom = x.getPrimitiveAt(0);
344
                }
345
            }
346
            break;
347
        case Geometry.TYPES.LINE:
348
            if( geom.getType()==Geometry.TYPES.MULTILINE ) {
349
                MultiLine x = (MultiLine) geom;
350
                if( x.getPrimitivesNumber()==1 ) {
351
                    geom = x.getPrimitiveAt(0);
352
                }
353
            }
354
            break;
355
        case Geometry.TYPES.POINT:
356
            if( geom.getType()==Geometry.TYPES.MULTIPOINT ) {
357
                MultiPoint x = (MultiPoint) geom;
358
                if( x.getPrimitivesNumber()==1 ) {
359
                    geom = x.getPrimitiveAt(0);
360
                }
361
            }
362
        }
363
        return geom;
364
    }
365

    
366
    @Override
367
    public JDBCServerExplorer createServerExplorer(JDBCServerExplorerParameters parameters, DataServerExplorerProviderServices providerServices) throws InitializeException {
368
        SpatiaLiteExplorer explorer = new SpatiaLiteExplorer(parameters, providerServices, this);
369
        return explorer;
370
    }
371

    
372
    @Override
373
    public JDBCStoreProvider createProvider(JDBCStoreParameters parameters, DataStoreProviderServices providerServices) throws InitializeException {
374
        return super.createProvider(parameters, providerServices); 
375
    }
376
    
377
    
378
}