Statistics
| Revision:

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

History | View | Annotate | Download (13.1 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.resource.exception.AccessResourceException;
35
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
36
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
37
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
38
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
39
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException;
40
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
41
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
42
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase;
43
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
44
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverBase;
45
import org.gvsig.fmap.geom.Geometry;
46
import org.gvsig.fmap.geom.aggregate.MultiLine;
47
import org.gvsig.fmap.geom.aggregate.MultiPoint;
48
import org.gvsig.fmap.geom.aggregate.MultiPolygon;
49
import org.gvsig.fmap.geom.exception.CreateGeometryException;
50
import org.gvsig.fmap.geom.primitive.Primitive;
51
import org.gvsig.fmap.geom.type.GeometryType;
52
import org.slf4j.Logger;
53
import org.slf4j.LoggerFactory;
54
import org.sqlite.SQLiteConfig;
55
import org.sqlite.SQLiteConfig.TransactionMode;
56

    
57
public class SpatiaLiteHelper extends JDBCHelperBase {
58

    
59
    static final Logger logger = LoggerFactory.getLogger(SpatiaLiteHelper.class);
60

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

    
89
    private static class ConnectionProvider {
90

    
91
        private static boolean needRegisterDriver = true;
92

    
93
        private BasicDataSource dataSource = null;
94

    
95
        private final SpatiaLiteConnectionParameters connectionParameters;
96

    
97
        public ConnectionProvider(SpatiaLiteConnectionParameters connectionParameters) {
98
            this.connectionParameters = connectionParameters;
99
        }
100

    
101
        public Connection getConnection() throws SQLException {
102
                boolean newDB = (connectionParameters.getFile()==null || !connectionParameters.getFile().exists());
103
            if (this.dataSource == null) {
104
                this.dataSource = this.createDataSource();               
105
            }
106
            Connection conn = this.dataSource.getConnection();
107
            loadExtension(conn);
108
            if (newDB) {
109
                    initSpatialMetadata(conn);
110
            }
111
            return conn;
112
        }
113

    
114
        private BasicDataSource createDataSource() throws SQLException {
115
            if (!this.isRegistered()) {
116
                this.registerDriver();
117
            }
118
            SpatiaLiteConnectionParameters params = connectionParameters;
119

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

    
140
        private boolean isRegistered() {
141
            return needRegisterDriver;
142
        }
143

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

    
195
    }
196

    
197
    private ConnectionProvider connectionProvider = null;
198
   
199
    public SpatiaLiteHelper(JDBCConnectionParameters connectionParameters) {
200
        super(connectionParameters);
201
        this.srssolver = new SRSSolverBase(this);
202
    }
203

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

    
228
    @Override
229
    protected String getResourceType() {
230
        return SpatiaLiteLibrary.NAME;
231
    }
232

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

    
238
    @Override
239
    public JDBCSQLBuilderBase createSQLBuilder() {
240
        return new SpatiaLiteSQLBuilder(this);
241
    }
242
    
243
    @Override
244
    public OperationsFactory getOperations() {
245
        if (this.operationsFactory == null) {
246
            this.operationsFactory = new SpatiaLiteOperationsFactory(this);
247
        }
248
        return operationsFactory;
249
    }
250

    
251
    @Override
252
    public SQLBuilder.GeometrySupportType getGeometrySupportType() {
253
        return SQLBuilder.GeometrySupportType.WKB;
254
    }
255

    
256
    @Override
257
    public boolean hasSpatialFunctions() {
258
        return true;
259
    }
260

    
261
    @Override
262
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
263
        return true;
264
    }
265

    
266
    @Override
267
    public String getQuoteForIdentifiers() {
268
        return "\"";
269
    }
270

    
271
    @Override
272
    public boolean allowAutomaticValues() {
273
        return true;
274
    }
275

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

    
281
    @Override
282
    public String getQuoteForStrings() {
283
        return "'";
284
    }
285

    
286
    @Override
287
    public String getSourceId(JDBCStoreParameters parameters) {
288
        return parameters.getDBName() + "." + 
289
               parameters.getSchema()+ "." + 
290
               parameters.getTable();
291
    }
292

    
293
    @Override
294
    public JDBCNewStoreParameters createNewStoreParameters() {
295
        return new SpatiaLiteNewStoreParameters();
296
    }
297

    
298
    @Override
299
    public JDBCStoreParameters createOpenStoreParameters() {
300
        return new SpatiaLiteStoreParameters();
301
    }
302

    
303
    @Override
304
    public JDBCServerExplorerParameters createServerExplorerParameters() {
305
        return new SpatiaLiteExplorerParameters();
306
    }
307

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