root / trunk / org.gvsig.postgresql / org.gvsig.postgresql.provider / src / main / java / org / gvsig / postgresql / dal / PostgreSQLHelper.java @ 1402
History | View | Annotate | Download (17.7 KB)
1 |
|
---|---|
2 |
package org.gvsig.postgresql.dal; |
3 |
|
4 |
import java.sql.Blob; |
5 |
import java.sql.Clob; |
6 |
import java.sql.Connection; |
7 |
import java.sql.DriverManager; |
8 |
import java.sql.ResultSet; |
9 |
import java.sql.SQLException; |
10 |
import java.util.HashMap; |
11 |
import java.util.Map; |
12 |
import org.apache.commons.dbcp2.BasicDataSource; |
13 |
import org.apache.commons.io.IOUtils; |
14 |
import org.apache.commons.lang3.ArrayUtils; |
15 |
import org.apache.commons.lang3.BooleanUtils; |
16 |
import org.apache.commons.lang3.StringUtils; |
17 |
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType; |
18 |
import org.gvsig.fmap.dal.DataTypes; |
19 |
import org.gvsig.fmap.dal.exception.DataException; |
20 |
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
21 |
import org.gvsig.fmap.dal.feature.spi.FeatureProvider; |
22 |
import org.gvsig.fmap.dal.resource.exception.AccessResourceException; |
23 |
import org.gvsig.fmap.dal.spi.DataTransactionServices; |
24 |
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters; |
25 |
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters; |
26 |
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters; |
27 |
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters; |
28 |
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException; |
29 |
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCDriverClassNotFoundException; |
30 |
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection; |
31 |
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils; |
32 |
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory; |
33 |
import org.gvsig.fmap.dal.store.jdbc2.spi.AbstractConnectionProvider; |
34 |
import org.gvsig.fmap.dal.store.jdbc2.spi.ConnectionProvider; |
35 |
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCConnectionBase; |
36 |
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase; |
37 |
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase; |
38 |
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverBase; |
39 |
import org.gvsig.fmap.dal.store.jdbc2.spi.SRSSolverDumb; |
40 |
import org.gvsig.fmap.geom.Geometry; |
41 |
import org.gvsig.postgresql.dal.operations.PostgreSQLOperationsFactory; |
42 |
import org.gvsig.tools.dispose.Disposable; |
43 |
import org.gvsig.tools.dispose.DisposeUtils; |
44 |
import org.gvsig.tools.exception.BaseException; |
45 |
import org.postgresql.util.DriverInfo; |
46 |
|
47 |
@SuppressWarnings("UseSpecificCatch") |
48 |
public class PostgreSQLHelper extends JDBCHelperBase { |
49 |
|
50 |
// /* friend */ static final Logger LOGGER = LoggerFactory.getLogger(PostgreSQLHelper.class);
|
51 |
|
52 |
public static final String POSTGRESQL_JDBC_DRIVER = "org.postgresql.Driver"; |
53 |
|
54 |
public static final boolean USE_CONNECTIONS_POOL = true; |
55 |
|
56 |
public static String getConnectionURL(PostgreSQLConnectionParameters params) { |
57 |
return getConnectionURL(
|
58 |
params.getHost(), |
59 |
params.getPort(), |
60 |
params.getDBName() |
61 |
); |
62 |
} |
63 |
|
64 |
public static String getConnectionURL(String host, Integer port, String db) { |
65 |
if( StringUtils.isEmpty(host) ) {
|
66 |
throw new IllegalArgumentException("Parameter 'host' can't be null."); |
67 |
} |
68 |
String connectionURL = "jdbc:postgresql://" + host; |
69 |
if (port != null) { |
70 |
connectionURL = connectionURL + ":" + port;
|
71 |
} |
72 |
connectionURL = connectionURL + "/" + db;
|
73 |
LOGGER.debug("connectionURL: {}", connectionURL);
|
74 |
return connectionURL;
|
75 |
} |
76 |
|
77 |
private static class PostgreSQLConnectionProvider extends AbstractConnectionProvider implements Disposable, ConnectionProvider { |
78 |
|
79 |
private static boolean needRegisterDriver = true; |
80 |
|
81 |
private BasicDataSource dataSource = null; |
82 |
|
83 |
private PostgreSQLConnectionParameters connectionParameters;
|
84 |
|
85 |
public PostgreSQLConnectionProvider(PostgreSQLConnectionParameters connectionParameters) {
|
86 |
this.connectionParameters = connectionParameters;
|
87 |
DisposeUtils.bind(this);
|
88 |
} |
89 |
|
90 |
@Override
|
91 |
public Connection getConnection() throws SQLException { |
92 |
|
93 |
Connection conn;
|
94 |
if( USE_CONNECTIONS_POOL ) {
|
95 |
if (this.dataSource == null) { |
96 |
this.dataSource = this.createDataSource(); |
97 |
} |
98 |
if( LOGGER.isDebugEnabled() ) {
|
99 |
LOGGER.debug("getConnection:\n" + getStatusInformation());
|
100 |
} |
101 |
try {
|
102 |
conn = this.dataSource.getConnection();
|
103 |
} catch(Throwable ex) { |
104 |
LOGGER.debug("Error getting connection from pool.",ex);
|
105 |
throw ex;
|
106 |
} |
107 |
try {
|
108 |
conn.setNetworkTimeout(null, this.connectionParameters.getNetworkTimeout()); |
109 |
} catch(Throwable ex) { |
110 |
LOGGER.warn("Error setting the network timeout.",ex);
|
111 |
} |
112 |
if( LOGGER.isDebugEnabled() ) {
|
113 |
LOGGER.debug("Created connection: {}\n NumActive: {}\n NumIdle: {}",
|
114 |
new Object[] { |
115 |
conn.hashCode(), |
116 |
this.dataSource.getNumActive(),
|
117 |
this.dataSource.getNumIdle()
|
118 |
} |
119 |
); |
120 |
} |
121 |
} else {
|
122 |
try {
|
123 |
conn = DriverManager.getConnection(
|
124 |
connectionParameters.getUrl(), |
125 |
connectionParameters.getUser(), |
126 |
connectionParameters.getPassword() |
127 |
); |
128 |
} catch(Throwable th) { |
129 |
throw th;
|
130 |
} |
131 |
if( LOGGER.isDebugEnabled() ) {
|
132 |
LOGGER.debug("Created not polled connection: {}",
|
133 |
new Object[] { |
134 |
conn.hashCode() |
135 |
} |
136 |
); |
137 |
} |
138 |
} |
139 |
LOGGER.debug("PostgreSQL JDBC Driver: "+DriverInfo.DRIVER_VERSION);
|
140 |
return conn;
|
141 |
} |
142 |
|
143 |
public String getStatusInformation() { |
144 |
StringBuilder builder = new StringBuilder(); |
145 |
if( this.dataSource==null ) { |
146 |
builder.append("Not poolled connection:\n");
|
147 |
builder.append(" Connection URL: '").append(this.connectionParameters.getUrl()).append("'\n"); |
148 |
} else {
|
149 |
builder.append("BasicDataSource pool status:\n");
|
150 |
builder.append(" Connection URL: '").append(this.dataSource.getUrl()).append("'\n"); |
151 |
if( this.dataSource.getInitialSize()>0 ) { |
152 |
builder.append(" InitialSize: ").append(this.dataSource.getInitialSize()).append(" (The initial number of connections that are created when the pool is started)\n"); |
153 |
} |
154 |
if( this.dataSource.isPoolPreparedStatements() ) { |
155 |
builder.append(" PoolPreparedStatements: ").append(this.dataSource.isPoolPreparedStatements()).append("\n"); |
156 |
builder.append(" MaxOpenPreparedStatements: ").append(this.dataSource.getMaxOpenPreparedStatements()).append(" (The maximum number of open statements that can be allocated from the statement pool at the same time, or non-positive for no limit)\n"); |
157 |
} |
158 |
builder.append(" MaxTotal: ").append(this.dataSource.getMaxTotal()).append(" (The maximum number of active connections that can be allocated from this pool at the same time)\n"); |
159 |
builder.append(" MaxIdle: ").append(this.dataSource.getMaxIdle()).append(" (The maximum number of connections that can remain idle in the pool)\n"); |
160 |
builder.append(" NumActive:").append(this.dataSource.getNumActive()).append(" (the current number of active connections)\n"); |
161 |
builder.append(" NumIdle:").append(this.dataSource.getNumIdle()).append(" (the current number of idle connections)\n"); |
162 |
} |
163 |
return builder.toString();
|
164 |
} |
165 |
|
166 |
private BasicDataSource createDataSource() throws SQLException { |
167 |
if (!this.isRegistered()) { |
168 |
this.registerDriver();
|
169 |
} |
170 |
PostgreSQLConnectionParameters params = connectionParameters; |
171 |
|
172 |
BasicDataSource ds = new BasicDataSource();
|
173 |
ds.setMaxIdle(params.getMaxIdle()); |
174 |
ds.setDriverClassName(params.getJDBCDriverClassName()); |
175 |
if( params.getUseSSL() ) {
|
176 |
String s = BooleanUtils.toStringTrueFalse(params.getUseSSL());
|
177 |
ds.addConnectionProperty("ssl", s );
|
178 |
} |
179 |
if( !StringUtils.isEmpty(params.getUser()) ) {
|
180 |
ds.setUsername(params.getUser()); |
181 |
} |
182 |
if( !StringUtils.isEmpty(params.getPassword()) ) {
|
183 |
ds.setPassword(params.getPassword()); |
184 |
} |
185 |
ds.setUrl(params.getUrl()); |
186 |
|
187 |
ds.setMinEvictableIdleTimeMillis(20*1000); |
188 |
ds.setTimeBetweenEvictionRunsMillis(20*1000); |
189 |
ds.setMinIdle(0);
|
190 |
|
191 |
ds.setMaxWaitMillis(60L * 1000); |
192 |
return ds;
|
193 |
} |
194 |
|
195 |
private boolean isRegistered() { |
196 |
return needRegisterDriver;
|
197 |
} |
198 |
|
199 |
@Override
|
200 |
public void registerDriver() throws SQLException { |
201 |
String className = this.connectionParameters.getJDBCDriverClassName(); |
202 |
if (className == null) { |
203 |
return;
|
204 |
} |
205 |
try {
|
206 |
Class theClass = Class.forName(className); |
207 |
if (theClass == null) { |
208 |
throw new JDBCDriverClassNotFoundException(PostgreSQLLibrary.NAME, className); |
209 |
} |
210 |
} catch (Exception e) { |
211 |
throw new SQLException("Can't register JDBC driver '" + className + "'.", e); |
212 |
} |
213 |
needRegisterDriver = false;
|
214 |
} |
215 |
|
216 |
@Override
|
217 |
public void dispose() { |
218 |
if(!DisposeUtils.release(this)){ |
219 |
return;
|
220 |
} |
221 |
if( this.dataSource!=null ) { |
222 |
try {
|
223 |
this.dataSource.close();
|
224 |
} catch (SQLException ex) { |
225 |
LOGGER.warn("Can't close BasicDataSource", ex);
|
226 |
} |
227 |
this.dataSource = null; |
228 |
} |
229 |
this.connectionParameters = null; |
230 |
} |
231 |
|
232 |
@Override
|
233 |
public boolean isDisposed() { |
234 |
return this.dataSource == null; |
235 |
} |
236 |
|
237 |
@Override
|
238 |
public String getStatus() { |
239 |
if( dataSource==null ) { |
240 |
return "Not polled"; |
241 |
} |
242 |
StringBuilder builder = new StringBuilder(); |
243 |
builder.append("Pool: ");
|
244 |
builder.append(JDBCUtils.getHexId(dataSource)); |
245 |
builder.append(" Actives: ");
|
246 |
builder.append(dataSource.getNumActive()); |
247 |
builder.append("/");
|
248 |
builder.append(dataSource.getMaxTotal()); //.getMaxActive());
|
249 |
builder.append(" idle: ");
|
250 |
builder.append(dataSource.getNumIdle()); |
251 |
builder.append("/");
|
252 |
builder.append(dataSource.getMinIdle()); |
253 |
builder.append(":");
|
254 |
builder.append(dataSource.getMaxIdle()); |
255 |
return builder.toString();
|
256 |
} |
257 |
|
258 |
} |
259 |
|
260 |
static private Map<String,ConnectionProvider> connectionProviders = new HashMap(); |
261 |
private ConnectionProvider connectionProvider = null; |
262 |
|
263 |
/**
|
264 |
* Constructor for use only for testing purposes.
|
265 |
*
|
266 |
* @param connectionParameters
|
267 |
* @param connectionProvider
|
268 |
*/
|
269 |
public PostgreSQLHelper(PostgreSQLConnectionParameters connectionParameters, ConnectionProvider connectionProvider) {
|
270 |
super(connectionParameters);
|
271 |
this.srssolver = new SRSSolverDumb(this); |
272 |
this.connectionProvider = connectionProvider;
|
273 |
} |
274 |
|
275 |
|
276 |
|
277 |
public PostgreSQLHelper(JDBCConnectionParameters connectionParameters) {
|
278 |
super(connectionParameters);
|
279 |
this.srssolver = new SRSSolverBase(this); |
280 |
} |
281 |
|
282 |
@Override
|
283 |
protected void doDispose() throws BaseException { |
284 |
if( this.connectionProvider!=null ) { |
285 |
this.connectionProvider.dispose();
|
286 |
this.connectionProvider = null; |
287 |
} |
288 |
super.doDispose();
|
289 |
} |
290 |
|
291 |
private ConnectionProvider getConnectionProvider(JDBCConnectionParameters connectionParameters) {
|
292 |
if (this.connectionProvider == null) { |
293 |
if (this.getConnectionParameters() == null) { |
294 |
return null; |
295 |
} |
296 |
String key = this.getConnectionProviderKey(connectionParameters); |
297 |
this.connectionProvider = connectionProviders.get(key);
|
298 |
if (this.connectionProvider == null || ((PostgreSQLConnectionProvider) this.connectionProvider).isDisposed()) { |
299 |
this.connectionProvider = new PostgreSQLConnectionProvider(this.getConnectionParameters()); |
300 |
connectionProviders.put(key, this.connectionProvider);
|
301 |
} else {
|
302 |
DisposeUtils.bind(this.connectionProvider);
|
303 |
} |
304 |
} |
305 |
return this.connectionProvider; |
306 |
} |
307 |
|
308 |
@Override
|
309 |
public synchronized JDBCConnection getConnection() throws AccessResourceException { |
310 |
try {
|
311 |
PostgreSQLConnectionParameters connectionParameters = this.getConnectionParameters();
|
312 |
JDBCConnection conn = (JDBCConnection) DataTransactionServices.getConnection( |
313 |
this.getTransaction(),
|
314 |
this.getConnectionProviderKey(connectionParameters)
|
315 |
); |
316 |
if( conn != null ) { |
317 |
return conn;
|
318 |
} |
319 |
JDBCConnection connection = new JDBCConnectionBase(
|
320 |
this.getTransaction(),
|
321 |
this.getConnectionProvider(connectionParameters).getConnection(),
|
322 |
this.getConnectionProviderKey(connectionParameters)
|
323 |
); |
324 |
return connection;
|
325 |
} catch (SQLException ex) { |
326 |
throw new AccessResourceException(PostgreSQLLibrary.NAME, ex); |
327 |
} |
328 |
} |
329 |
|
330 |
@Override
|
331 |
public PostgreSQLConnectionParameters getConnectionParameters() {
|
332 |
return (PostgreSQLConnectionParameters) super.getConnectionParameters(); |
333 |
} |
334 |
|
335 |
@Override
|
336 |
public String getConnectionURL() { |
337 |
return getConnectionURL(this.getConnectionParameters()); |
338 |
} |
339 |
|
340 |
@Override
|
341 |
protected String getResourceType() { |
342 |
return PostgreSQLLibrary.NAME;
|
343 |
} |
344 |
|
345 |
@Override
|
346 |
public String getProviderName() { |
347 |
return PostgreSQLLibrary.NAME;
|
348 |
} |
349 |
|
350 |
@Override
|
351 |
public JDBCSQLBuilderBase createSQLBuilder() {
|
352 |
return new PostgreSQLBuilder(this); |
353 |
} |
354 |
|
355 |
@Override
|
356 |
public OperationsFactory getOperations() {
|
357 |
if (this.operationsFactory == null) { |
358 |
this.operationsFactory = new PostgreSQLOperationsFactory(this); |
359 |
} |
360 |
return operationsFactory;
|
361 |
} |
362 |
|
363 |
@Override
|
364 |
public GeometrySupportType getGeometrySupportType() {
|
365 |
return GeometrySupportType.EWKB;
|
366 |
} |
367 |
|
368 |
@Override
|
369 |
public boolean hasSpatialFunctions() { |
370 |
return true; |
371 |
} |
372 |
|
373 |
@Override
|
374 |
public boolean canWriteGeometry(int geometryType, int geometrySubtype) { |
375 |
return true; |
376 |
} |
377 |
|
378 |
@Override
|
379 |
public String getQuoteForIdentifiers() { |
380 |
return "\""; |
381 |
} |
382 |
|
383 |
@Override
|
384 |
public boolean allowAutomaticValues() { |
385 |
return true; |
386 |
} |
387 |
|
388 |
@Override
|
389 |
public boolean supportOffsetInSelect() { |
390 |
return true; |
391 |
} |
392 |
|
393 |
@Override
|
394 |
public String getQuoteForStrings() { |
395 |
return "'"; |
396 |
} |
397 |
|
398 |
@Override
|
399 |
public JDBCNewStoreParameters createNewStoreParameters() {
|
400 |
return new PostgreSQLNewStoreParameters(); |
401 |
} |
402 |
|
403 |
@Override
|
404 |
public JDBCStoreParameters createOpenStoreParameters() {
|
405 |
return new PostgreSQLStoreParameters(); |
406 |
} |
407 |
|
408 |
@Override
|
409 |
public JDBCServerExplorerParameters createServerExplorerParameters() {
|
410 |
return new PostgreSQLServerExplorerParameters(); |
411 |
} |
412 |
|
413 |
@Override
|
414 |
public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException { |
415 |
Object value;
|
416 |
try {
|
417 |
int rsIndex = 1; |
418 |
for (FeatureAttributeDescriptor column : columns) {
|
419 |
switch (column.getType()) {
|
420 |
case DataTypes.GEOMETRY:
|
421 |
value = this.getGeometryFromColumn(rs, rsIndex++);
|
422 |
if (value != null){ |
423 |
((Geometry)value).setProjection(column.getSRS()); |
424 |
} |
425 |
break;
|
426 |
case DataTypes.BYTEARRAY:
|
427 |
value = rs.getBytes(rsIndex++); |
428 |
break;
|
429 |
default:
|
430 |
value = rs.getObject(rsIndex++); |
431 |
if (value instanceof Blob) { |
432 |
Blob blob = (Blob) value; |
433 |
value = blob.getBytes(1, (int) blob.length()); |
434 |
blob.free(); |
435 |
} else if (value instanceof Clob) { |
436 |
Clob clob = (Clob) value; |
437 |
value = new String(IOUtils.toCharArray(clob.getCharacterStream())); |
438 |
clob.free(); |
439 |
} |
440 |
} |
441 |
feature.set(column.getIndex(), value); |
442 |
} |
443 |
if (ArrayUtils.isNotEmpty(extraValueNames)) {
|
444 |
feature.setExtraValueNames(extraValueNames); |
445 |
for (int index = 0; index < extraValueNames.length; index++) { |
446 |
value = rs.getObject(rsIndex++); |
447 |
if (value instanceof Blob) { |
448 |
Blob blob = (Blob) value; |
449 |
value = blob.getBytes(0, (int) blob.length()); |
450 |
blob.free(); |
451 |
} |
452 |
feature.setExtraValue(index, value); |
453 |
} |
454 |
} |
455 |
} catch (Exception ex) { |
456 |
throw new JDBCCantFetchValueException(ex); |
457 |
} |
458 |
} |
459 |
|
460 |
} |