Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc2 / spi / JDBCStoreProviderBase.java @ 45065

History | View | Annotate | Download (21.1 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
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
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.jdbc2.spi;
25

    
26
import java.text.MessageFormat;
27
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
28
import java.util.Arrays;
29
import java.util.Collections;
30
import java.util.Iterator;
31
import java.util.List;
32
import org.apache.commons.lang3.BooleanUtils;
33
import org.apache.commons.lang3.StringUtils;
34
import org.cresques.cts.IProjection;
35
import org.gvsig.fmap.dal.DALLocator;
36
import org.gvsig.fmap.dal.DataManager;
37
import org.gvsig.fmap.dal.DataServerExplorer;
38
import org.gvsig.fmap.dal.DataStore;
39
import org.gvsig.fmap.dal.DataStoreNotification;
40
import org.gvsig.fmap.dal.DataTypes;
41
import org.gvsig.fmap.dal.exception.CloseException;
42
import org.gvsig.fmap.dal.exception.DataException;
43
import org.gvsig.fmap.dal.exception.InitializeException;
44
import org.gvsig.fmap.dal.exception.OpenException;
45
import org.gvsig.fmap.dal.exception.ReadException;
46
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
47
import org.gvsig.fmap.dal.feature.EditableFeatureType;
48
import org.gvsig.fmap.dal.feature.FeatureQuery;
49
import org.gvsig.fmap.dal.feature.FeatureRule;
50
import org.gvsig.fmap.dal.feature.FeatureRules;
51
import org.gvsig.fmap.dal.feature.FeatureType;
52
import org.gvsig.fmap.dal.feature.spi.AbstractFeatureStoreProvider;
53
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
54
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
55
import org.gvsig.fmap.dal.feature.spi.FeatureSetProvider;
56
import org.gvsig.fmap.dal.resource.ResourceParameters;
57
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
58
import org.gvsig.fmap.dal.resource.exception.ResourceException;
59
import org.gvsig.fmap.dal.resource.spi.AbstractResource;
60
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
61
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
62
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
63
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
64
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
65
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
66
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
67
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
68
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
69
import org.gvsig.fmap.dal.store.jdbc2.impl.JDBCSetProvider;
70
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.AppendOperation;
71
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.CalculateEnvelopeOfColumnOperation;
72
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.CanModifyTableOperation;
73
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.CountOperation;
74
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.FetchFeatureProviderByReferenceOperation;
75
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.FetchFeatureTypeOperation;
76
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.PerformChangesOperation;
77
import org.gvsig.fmap.geom.Geometry;
78
import org.gvsig.fmap.geom.primitive.Envelope;
79
import org.gvsig.tools.dynobject.DynField;
80
import org.gvsig.tools.dynobject.DynObject;
81
import org.gvsig.tools.dynobject.exception.DynFieldNotFoundException;
82
import org.gvsig.tools.exception.BaseException;
83
import org.slf4j.Logger;
84
import org.slf4j.LoggerFactory;
85

    
86
@SuppressWarnings("UseSpecificCatch")
87
public class JDBCStoreProviderBase
88
        extends AbstractFeatureStoreProvider
89
        implements ResourceConsumer, JDBCStoreProvider {
90

    
91
    final static private Logger LOGGER = LoggerFactory.getLogger(JDBCStoreProviderBase.class);
92

    
93
    public class CountValue implements CalculatedValue<Long> {
94
        
95
        private Long value = null;
96

    
97
        @Override
98
        public void calculate() {
99
            try {
100
                JDBCStoreParameters params = getParameters();
101
                CountOperation count = getOperations().createCount(
102
                        getFeatureStore().getDefaultFeatureType(),
103
                        getOperations().createTableReference(params),
104
                        params.getBaseFilter(),
105
                        null
106
                );            
107
                this.value = (Long) count.perform();
108
            } catch (DataException ex) {
109
                throw new RuntimeException("Can't calculate count",ex);
110
            }
111
        }
112
        
113
        @Override
114
        public void reset() {
115
            this.value = null;
116
        }
117
        
118
        @Override
119
        public Long get() {
120
            if( this.value == null ) {
121
                this.calculate();
122
            }
123
            return this.value;
124
        }
125
    } 
126
    
127
    public class EnvelopeValue implements CalculatedValue<Envelope> {
128
        
129
        private Envelope value = null;
130
        private boolean needCalculate = true;
131

    
132
        @Override
133
        public void calculate() {
134
            try {
135
                value = null;
136
                String columnName = getFeatureStore()
137
                        .getDefaultFeatureType()
138
                        .getDefaultGeometryAttributeName();
139
                if( columnName==null ) {
140
                    return;
141
                }
142
                IProjection crs = getFeatureStore()
143
                        .getDefaultFeatureType()
144
                        .getDefaultSRS();
145
                JDBCStoreParameters params = getParameters();
146
                CalculateEnvelopeOfColumnOperation calculateEnvelopeOfColumn = 
147
                    getOperations().createCalculateEnvelopeOfColumn(
148
                        getFeatureStore().getDefaultFeatureType(),
149
                        getOperations().createTableReference(params),
150
                        columnName, 
151
                        params.getBaseFilter(), 
152
                        params.getWorkingArea(), 
153
                        crs
154
                    );
155
                value = (Envelope) calculateEnvelopeOfColumn.perform();
156
                
157
            } catch(Exception ex) {
158
                throw new RuntimeException("Can't calculate envelope.", ex);
159
            } finally {
160
               needCalculate = false;
161
            }
162
        }
163
        
164
        @Override
165
        public void reset() {
166
            this.value = null;
167
            this.needCalculate = true;
168
        }
169
        
170
        @Override
171
        public synchronized Envelope get() {
172
            if( needCalculate ) {
173
                this.calculate();
174
            }
175
            return this.value;
176
        }
177
    } 
178
        
179
    public class AllowWriteValue implements CalculatedValue<Boolean> {
180
        
181
        private Boolean value = null;
182

    
183
        @Override
184
        public void calculate() {
185
            try {
186
                JDBCStoreParameters params = getParameters();
187
                CanModifyTableOperation canModifyTable = 
188
                    getOperations().createCanModifyTableOperation(
189
                        getOperations().createTableReference(params)
190
                    );
191
                this.value = (boolean) canModifyTable.perform();
192
            } catch(Exception ex) {
193
                throw new RuntimeException("Can't determine if allow write.", ex);
194
            }
195
        }
196
        
197
        @Override
198
        public void reset() {
199
            this.value = null;
200
        }
201
        
202
        @Override
203
        public Boolean get() {
204
            if( this.value == null ) {
205
                this.calculate();
206
            }
207
            return this.value;
208
        }
209
    } 
210
    
211
    protected final JDBCHelper helper;
212

    
213
    protected CalculatedValue<Long> count = null;
214
    
215
    protected CalculatedValue<Envelope> envelope = null;
216

    
217
    protected CalculatedValue<Boolean> allowWrite = null;
218

    
219
    protected AppendOperation appendOperation = null;
220
    
221
    @SuppressWarnings({"OverridableMethodCallInConstructor", "CallToThreadStartDuringObjectConstruction"})
222
    protected JDBCStoreProviderBase(
223
            JDBCStoreParameters params,
224
            DataStoreProviderServices storeServices,
225
            DynObject metadata,
226
            JDBCHelper helper
227
    ) throws InitializeException {
228
        super(params, storeServices, metadata);
229
        this.helper = helper;
230
        this.initializeFeatureType();
231
        try {
232
            if( BooleanUtils.isTrue((Boolean) params.getDynValue("precalculateEnvelope"))  ) {
233
                FeatureType featureType = this.getStoreServices().getDefaultFeatureType();
234
                if( !StringUtils.isEmpty(featureType.getDefaultGeometryAttributeName()) ) {
235
                    Thread thread = new Thread(() -> {
236
                        LOGGER.trace("Precalculating envelope of '"+getSourceId()+"'.");
237
                        getEnvelopeValue().get();
238
                    }, "PrecalculateEnvelopeOfDBTable");
239
                    thread.start();
240
                    Thread.sleep(1);
241
                }
242
           }
243
        } catch(Exception ex) {
244
            LOGGER.warn("Probems precalculating the envelope of table '"+this.getSourceId()+"'.", ex);
245
        }
246
    }
247

    
248
    @Override
249
    public JDBCStoreParameters getParameters() {
250
        return (JDBCStoreParameters) super.getParameters();
251
    }  
252

    
253
    @Override
254
    public JDBCHelper getHelper() {
255
        return helper;
256
    }
257
    
258
    public OperationsFactory getOperations() {
259
        return this.getHelper().getOperations();
260
    }
261
    
262
    @Override
263
    public String getProviderName() {
264
        return this.getHelper().getProviderName();
265
    }
266
    
267
    @Override
268
    public int getOIDType() {
269
        return DataTypes.UNKNOWN;
270
    }
271

    
272
    @Override
273
    public Object createNewOID() {
274
        return null;
275
    }
276
    
277
    @Override
278
    public boolean allowAutomaticValues() {
279
        return this.getHelper().allowAutomaticValues();
280
    }
281

    
282
    @Override
283
    public boolean allowWrite() {
284
        return this.getAllowWriteValue().get();
285
    }
286
    
287
    @Override
288
    public Object getDynValue(String name) throws DynFieldNotFoundException {
289
        try {
290
            if (DataStore.METADATA_ENVELOPE.equalsIgnoreCase(name)) {
291
                Envelope env = this.getEnvelope();
292
                if (env != null) {
293
                    return env;
294
                }
295
            } else if (DataStore.METADATA_CRS.equalsIgnoreCase(name)) {
296
                IProjection proj;
297
                proj = this.getFeatureStore().getDefaultFeatureType().getDefaultSRS();
298
                if (proj != null) {
299
                    return proj;
300
                }
301
            }
302
        } catch (DataException e) {
303
            throw new RuntimeException(e);
304
        }
305
        return super.getDynValue(name);
306
    }
307

    
308
    @Override
309
    public CalculatedValue<Long> getCountValue() {
310
        if( this.count == null ) {
311
            this.count = new CountValue();
312
        }
313
        return this.count;
314
    }
315

    
316
    @Override
317
    public CalculatedValue<Envelope> getEnvelopeValue() {
318
        if( this.envelope == null ) {
319
            this.envelope = new EnvelopeValue();
320
        }
321
        return this.envelope;
322
    }
323
    
324
    @Override
325
    public CalculatedValue<Boolean> getAllowWriteValue() {
326
        if( this.allowWrite == null ) {
327
            this.allowWrite = new AllowWriteValue();
328
        }
329
        return this.allowWrite;
330
    }
331
    
332
    @Override
333
    public long getFeatureCount() throws DataException {
334
        return this.getCountValue().get();
335
    }
336
    
337
    @Override
338
    public boolean closeResourceRequested(ResourceProvider resource) {
339
        ResulSetControler resulSetControler = this.getHelper().getResulSetControler();
340
        resulSetControler.pack();
341
        return resulSetControler.getOpenCount() == 0;
342
    }
343

    
344
    @Override
345
    public void close() throws CloseException {
346
        JDBCUtils.closeQuietly(this.getHelper());
347
    }
348

    
349
    @Override
350
    public void resourceChanged(ResourceProvider resource) {
351
        this.getStoreServices().notifyChange(
352
                DataStoreNotification.RESOURCE_CHANGED,
353
                resource
354
        );
355
    }
356

    
357
    @Override
358
    public DataServerExplorer getExplorer() throws ReadException {
359
        DataManager manager = DALLocator.getDataManager();
360
        JDBCServerExplorerParameters exParams;
361
        JDBCStoreParameters params = getParameters();
362
        try {
363
            exParams = this.getHelper().createServerExplorerParameters();
364
            DynField[] fields = exParams.getDynClass().getDynFields();
365
            for (DynField field : fields) {
366
                try {
367
                    exParams.setDynValue(field.getName(), params.getDynValue(field.getName()));
368
                } catch(Exception ex) {
369
                    // Ignore
370
                }
371
            }
372
            exParams.setHost(params.getHost());
373
            exParams.setPort(params.getPort());
374
            exParams.setDBName(params.getDBName());
375
            exParams.setUser(params.getUser());
376
            exParams.setPassword(params.getPassword());
377
            exParams.setUrl(params.getUrl());
378
            exParams.setCatalog(params.getCatalog());
379
            exParams.setSchema(params.getSchema());
380
            exParams.setJDBCDriverClassName(params.getJDBCDriverClassName());
381

    
382
            return manager.openServerExplorer(exParams.getExplorerName(), exParams);
383
        } catch (Exception e) {
384
            throw new ReadException(this.getProviderName(), e);
385
        }
386
    }
387

    
388
    @Override
389
    protected void doDispose() throws BaseException {
390
        this.close();
391
        this.getHelper().dispose();
392
        super.doDispose();
393
    }
394

    
395
    @Override
396
    public String getSourceId() {
397
        try {
398
            return this.getHelper().getSourceId(this.getParameters());
399
        } catch(Exception ex) {
400
            return "unknow";
401
        }
402
    }
403

    
404
    @Override
405
    public String getName() {
406
        return this.getParameters().getTable();
407
    }
408

    
409
    @Override
410
    public String getFullName() {
411
        return this.getHelper().getSourceId(this.getParameters());
412
    }
413

    
414
    private static class DummyResource extends AbstractResource {
415

    
416
        private final String name;
417

    
418
        DummyResource(String name) throws InitializeException {
419
            super((ResourceParameters)null);
420
            this.name = name;
421
        }
422
        
423
        @Override
424
        public String getName() throws AccessResourceException {
425
            return MessageFormat.format("DummyResource({0})",
426
                                new Object[] { this.name });
427
        }
428

    
429
        @Override
430
        public Object get() throws AccessResourceException {
431
            return null;
432
        }
433

    
434
        @Override
435
        public boolean isThis(ResourceParameters parameters) throws ResourceException {
436
            return true;
437
        }
438
        
439
    }
440
    
441
    @Override
442
    public ResourceProvider getResource() {
443
        ResourceProvider r = getHelper().getResource();
444
        if( r == null ) {
445
            try {
446
                r = new DummyResource(this.getName());
447
            } catch (InitializeException ex) {
448
                LOGGER.warn("Can't create DummyResource",ex);
449
                // Do nothing
450
            }
451
        }
452
        return r;
453
    }
454

    
455
    @Override
456
    public void open() throws OpenException {
457

    
458
    }
459

    
460
    @Override
461
    public FeatureSetProvider createSet(
462
            FeatureQuery query,
463
            FeatureType featureType
464
        ) throws DataException {
465
        
466
        FeatureSetProvider set = new JDBCSetProvider(
467
                this,
468
                this.getHelper(), 
469
                query, 
470
                featureType
471
        );
472
        
473
        return set;
474
    }
475

    
476
    protected void initializeFeatureType() {
477
        EditableFeatureType type = this.getStoreServices().createFeatureType(getName());
478
        JDBCStoreParameters params = this.getParameters();
479
        List<String> primaryKeys = null;
480
        if( params.getPkFields() != null ) {
481
            primaryKeys = Arrays.asList(params.getPkFields());
482
        }
483
        FetchFeatureTypeOperation fetchFeatureType = 
484
             this.getOperations().createFetchFeatureType(
485
                type,
486
                this.getOperations().createTableReference(params),
487
                primaryKeys,
488
                params.getDefaultGeometryField(),
489
                params.getCRS()
490
            );
491
        fetchFeatureType.perform();
492

    
493
        if (!StringUtils.isBlank(params.getDefaultGeometryField())) {
494
            if (!params.getDefaultGeometryField().equalsIgnoreCase(type.getDefaultGeometryAttributeName())) {
495
                if (type.getAttributeDescriptor(params.getDefaultGeometryField()) != null) {
496
                    type.setDefaultGeometryAttributeName(params.getDefaultGeometryField());
497
                } else {
498
                    type.setDefaultGeometryAttributeName(null);
499
                }
500
                if (type.getDefaultGeometryAttribute() != null) {
501
                    EditableFeatureAttributeDescriptor attr = (EditableFeatureAttributeDescriptor) type.getDefaultGeometryAttribute();
502
                    attr.setGeometryType(Geometry.TYPES.GEOMETRY, Geometry.SUBTYPES.GEOM2D);
503
                }
504
            } else {
505
                type.setDefaultGeometryAttributeName(null);
506
            }
507
        }
508
        
509
        FeatureType defaultType = type.getNotEditableCopy();
510
        List<FeatureType> types = Collections.singletonList(defaultType);
511
        this.getStoreServices().setFeatureTypes(types, defaultType);
512
    }
513
    
514
    @Override
515
    protected FeatureProvider internalGetFeatureProviderByReference(
516
            FeatureReferenceProviderServices reference, 
517
            FeatureType featureType
518
        ) throws DataException {
519
        JDBCStoreParameters params = this.getParameters();
520
        FetchFeatureProviderByReferenceOperation fetchFeatureProviderByReference = 
521
            this.getOperations().createFetchFeatureProviderByReference(
522
                reference, 
523
                featureType, 
524
                this.getOperations().createTableReference(params)
525
            );
526
        FeatureProvider feature = (FeatureProvider) fetchFeatureProviderByReference.perform();
527
        return feature;
528
    }
529
    
530
    @Override
531
    public Envelope getEnvelope() throws DataException {
532
        return this.getEnvelopeValue().get();
533
    }
534

    
535
    @Override
536
    public void performChanges(Iterator deleteds, Iterator inserteds,
537
                    Iterator updateds, Iterator featureTypesChanged)
538
                    throws DataException {
539

    
540
        FeatureType type = this.getFeatureStore().getDefaultFeatureType();
541
        JDBCStoreParameters params = this.getParameters();
542
        PerformChangesOperation performChanges = this.getOperations().createPerformChanges(
543
                this.getOperations().createTableReference(params),
544
                type, 
545
                deleteds, 
546
                inserteds, 
547
                updateds, 
548
                featureTypesChanged
549
        );
550
        performChanges.perform();
551
        if( performChanges.isTypeChanged() ) {
552
            // Get rules before initializing feature type
553
            FeatureRules saved_rules = getFeatureStore().getDefaultFeatureType().getRules();
554

    
555
             // This initialization loses the feature type rules
556
            this.initializeFeatureType();
557

    
558
            // Get new feature type, clear rules and add the ones saved previously
559
            FeatureType featureType = getFeatureStore().getDefaultFeatureType();
560
            FeatureRules rules = featureType.getRules();
561
            rules.clear();
562
            for (FeatureRule rule : saved_rules) {
563
                rules.add(rule);
564
            }
565
        }
566
        this.getCountValue().reset();
567
        this.getEnvelopeValue().reset();
568
    }    
569
    
570
    @Override
571
    public boolean supportsAppendMode() {
572
        return true;
573
    }
574

    
575
    protected AppendOperation getAppendOperation() throws DataException {
576
        if( this.appendOperation == null ) {
577
            FeatureType type = this.getFeatureStore().getDefaultFeatureType();
578
            JDBCStoreParameters params = this.getParameters();
579
            this.appendOperation = this.getOperations().createAppend(
580
                this.getOperations().createTableReference(params),
581
                type 
582
            );
583
        }
584
        return this.appendOperation;
585
    }
586
    
587
    @Override
588
    public void endAppend() throws DataException {
589
        this.getAppendOperation().end();
590
    }
591

    
592
    @Override
593
    public void abortAppend() throws DataException {
594
        this.getAppendOperation().abort();
595
    }
596
    
597
    @Override
598
    public void beginAppend() throws DataException {
599
        this.getAppendOperation().begin();
600
    }
601

    
602
    @Override
603
    public void append(final FeatureProvider featureProvider) throws DataException {
604
        this.getAppendOperation().append(featureProvider);
605
    }    
606
    
607
    @Override
608
    public boolean canWriteGeometry(int geometryType, int geometrySubtype)
609
            throws DataException {
610
        return this.getHelper().canWriteGeometry(geometryType,geometrySubtype);
611
    }
612
}