Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / fmap / dal / impl / DefaultTransaction.java @ 47673

History | View | Annotate | Download (14.6 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, see <https://www.gnu.org/licenses/>. 
18
 * 
19
 * For any additional information, do not hesitate to contact us
20
 * at info AT gvsig.com, or visit our website www.gvsig.com.
21
 */
22

    
23
package org.gvsig.fmap.dal.impl;
24

    
25
import java.util.ArrayList;
26
import java.util.Collection;
27
import java.util.HashMap;
28
import java.util.HashSet;
29
import java.util.List;
30
import java.util.Map;
31
import java.util.Set;
32
import java.util.UUID;
33
import org.apache.commons.lang3.StringUtils;
34
import org.apache.commons.lang3.tuple.MutablePair;
35
import org.apache.commons.lang3.tuple.Pair;
36
import org.gvsig.fmap.dal.DataServerExplorer;
37
import org.gvsig.fmap.dal.DataStore;
38
import org.gvsig.fmap.dal.SupportTransactions;
39
import org.gvsig.fmap.dal.exception.DataException;
40
import org.gvsig.fmap.dal.feature.FeatureStore;
41
import static org.gvsig.fmap.dal.feature.FeatureStore.MODE_QUERY;
42
import org.gvsig.fmap.dal.spi.DataTransactionServices;
43
import org.gvsig.tools.dispose.Disposable;
44
import org.gvsig.tools.dispose.DisposeUtils;
45
import org.gvsig.tools.dispose.impl.AbstractDisposable;
46
import org.gvsig.tools.observer.BaseNotification;
47
import org.gvsig.tools.observer.ObservableHelper;
48
import org.gvsig.tools.observer.Observer;
49
import org.slf4j.Logger;
50
import org.slf4j.LoggerFactory;
51

    
52
/**
53
 *
54
 * @author gvSIG Team
55
 */
56
@SuppressWarnings("UseSpecificCatch")
57
public class DefaultTransaction extends AbstractDisposable implements DataTransactionServices {
58

    
59
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTransaction.class);
60
    
61
    private final String code;
62
    private final Map<String,DataServerExplorer> explorers;
63
    private final Map<String,ConnectionService> connections;
64
    private Map<String,Pair<DataStore,Boolean>> stores;
65
    private boolean inProgress;
66
    private List<Disposable> disposables;
67
    private ObservableHelper observableHelper;
68
    private Set<SupportTransactions> supportTransactions;
69

    
70
    public DefaultTransaction() {
71
        this.code = UUID.randomUUID().toString().replace("-", "");
72
        this.stores = new HashMap<>();
73
        this.explorers = new HashMap<>();
74
        this.disposables = new ArrayList<>();
75
        this.supportTransactions = new HashSet<>();
76
        this.inProgress = false;
77
        this.connections = new HashMap<>();
78
        this.observableHelper = new ObservableHelper();
79
    }
80

    
81
    @Override
82
    public String getCode() {
83
        return this.code;
84
    }
85

    
86
    @Override
87
    public void begin() throws DataException {
88
        if( this.inProgress ) {
89
            throw new IllegalStateException("Transaction already started.");
90
        }
91
        this.observableHelper.notifyObservers(this, new BaseNotification("BEGIN", null));
92
        this.inProgress = true;
93
    }
94

    
95
    @Override
96
    public void commit() throws DataException {
97
        if (!this.isInProgress()) {
98
            throw new IllegalStateException("Can't commit transaction without begin.");
99
        }
100
        try {
101
            LOGGER.debug("commit in "+this.getCode());
102
            int retries = 5;
103
            boolean needretry = false;
104
            while(retries > 0) {
105
                needretry = false;
106
                Collection<Pair<DataStore, Boolean>> theStores = new ArrayList(stores.values());
107
                for (Pair<DataStore, Boolean> item : theStores) {
108
                    DataStore store = getStore(item);
109
                    if (store instanceof FeatureStore) {
110
                        FeatureStore fstore = (FeatureStore) store;
111
                        if (fstore != null && fstore.getMode() != MODE_QUERY) {
112
                            LOGGER.debug("commit: finishEditing "+store.getFullName());
113
                            fstore.finishEditing();
114
                            needretry=true;
115
                        }
116
                    }
117
                }
118
                if( !needretry ) {
119
                    break;
120
                }
121
                LOGGER.debug("commit: retry store finish editing");
122
                retries--;
123
            }
124
            for (ConnectionService connection : this.connections.values()) {
125
                connection.finish();
126
            }
127
            this.observableHelper.notifyObservers(this, new BaseNotification("COMMIT", null));
128
            this.inProgress = false;
129
        } finally {
130
            LOGGER.debug("commit out "+this.getCode());
131
        }
132
    }
133

    
134
    @Override
135
    public void rollback() throws DataException {
136
        if( !this.isInProgress() ) {
137
            throw new IllegalStateException("Can't rollback transaction without begin.");
138
        }
139
//        LOGGER.info("rollback in");
140
        for (Pair<DataStore, Boolean> item : stores.values()) {
141
            DataStore store = getStore(item);
142
            if( store instanceof FeatureStore ) {
143
                FeatureStore fstore = (FeatureStore) store;
144
                if( fstore.getMode() != MODE_QUERY) {
145
                    fstore.cancelEditing();
146
                }
147
            }
148
        }
149
        for (ConnectionService connection : this.connections.values()) {
150
            connection.abort();
151
        }
152
        this.observableHelper.notifyObservers(this, new BaseNotification("ROLLBACK", null));
153
        this.inProgress = false;
154
//        LOGGER.info("rollback out");
155
    }
156

    
157
    @Override
158
    public void rollbackQuietly() {
159
        try {
160
            this.rollback();
161
        } catch(Exception ex) {
162
            // Do nothing
163
        }
164
    }
165

    
166
    @Override
167
    public void add(DataStore store) {
168
        add(store, null, true);
169
    }
170

    
171
    @Override
172
    public void add(DataStore store, String id) { 
173
        this.add(store, id, true);
174
    }
175
    
176
    @Override
177
    public void add(DataStore store, boolean local) {
178
        this.add(store, null, local);
179
    }
180
    
181
    @Override
182
    public void add(DataStore store, String id, boolean local) {
183
        if (store == null) {
184
            throw new IllegalArgumentException("The store is required.");
185
        }
186
//        try {
187
//            LOGGER.info("add in "+this.getCode()+" "+store.getFullName());
188
            String theId = id;
189
            if (StringUtils.isBlank(id)) {
190
                theId = store.hashCode() + "@" + store.getFullName();
191
            } else {
192
                DataStore theStore = getStore(this.stores.get(theId));
193
                if (theStore != null) {
194
                    if (theStore == store) {
195
                        return;
196
                    }
197
                    throw new IllegalArgumentException("The id '" + id + "' is already used.");
198
                }
199
            }
200
            if (store instanceof SupportTransactions) {
201
                ((SupportTransactions) store).setTransaction(this);
202
            }
203
            if (!local) {
204
                DisposeUtils.bind(store);
205
            }
206
            this.stores.put(theId, new MutablePair<>(store, local));
207
//        } finally {
208
//            LOGGER.info("add out");
209
//        }
210
    }
211

    
212
    @Override
213
    public FeatureStore getFeatureStore(String id) {
214
        return (FeatureStore) this.getStore(this.stores.get(id));
215
    }
216
    
217
    @Override
218
    public void add(DataServerExplorer explorer) {
219
        this.add(explorer, null, true);
220
    }
221

    
222
    @Override
223
    public void add(DataServerExplorer explorer, String id) {
224
        this.add(explorer, id, true);
225
    }
226
    
227
    @Override
228
    public void add(DataServerExplorer explorer, boolean local) {
229
        this.add(explorer, null, local);
230
    }
231
    
232
    @Override
233
    public void add(DataServerExplorer explorer, String id, boolean local) {
234
        if(explorer == null){
235
            throw new IllegalArgumentException("The explorer is required.");
236
        }
237
        String theId = id;
238
        if( StringUtils.isBlank(id) ) {
239
            theId = String.valueOf(explorer.hashCode());
240
        } else {
241
            DataServerExplorer theExplorer = this.explorers.get(theId);
242
            if(theExplorer!=null ){
243
                if( theExplorer==explorer ) {
244
                    return;
245
                }
246
                throw new IllegalArgumentException("The id '"+id+"' is already used.");
247
            }
248
        }
249
        
250
        if( explorer instanceof SupportTransactions ) {
251
            ((SupportTransactions) explorer).setTransaction(this);
252
        }
253
        if(!local){
254
            DisposeUtils.bind(explorer);
255
        }
256
        this.explorers.put(theId,explorer);
257
    }
258
    
259
    @Override
260
    public DataServerExplorer getServerExplorer(String id) {
261
        return this.explorers.get(id);
262
    }
263

    
264
    @Override
265
    public void add(Disposable resource) throws DataException {
266
        this.disposables.add(resource);
267
    }
268

    
269
    @Override
270
    public void add(SupportTransactions obj, boolean local) throws DataException {
271
        if(obj == null){
272
            throw new IllegalArgumentException("The transaction supplier is required.");
273
        }
274
        if(obj instanceof DataStore){
275
          this.add((DataStore)obj, local);
276
          return;
277
        } 
278
        if(obj instanceof DataServerExplorer){
279
          this.add((DataServerExplorer)obj, local);
280
          return;
281
        } 
282
        obj.setTransaction(this);
283
        if(!local){
284
            DisposeUtils.bind(obj);
285
        }
286
        this.supportTransactions.add(obj);
287
    }
288

    
289
    @Override
290
    public void remove(DataStore store) {
291
        if( this.inProgress && !DisposeUtils.isNullOrDisposed(store)){
292
            throw new IllegalStateException("Can't remove store from a in progress transaction.");
293
        }
294
//        try {
295
//            LOGGER.info("remove in");        
296
            String id = null;
297
            for (Map.Entry<String, Pair<DataStore,Boolean>> entry : this.stores.entrySet()) {
298
                if( store == getStore(entry.getValue()) ) {
299
                    id = entry.getKey();
300
                    break;
301
                }
302
            }
303
            if( id==null ) {
304
                return;
305
            }
306
            if( store instanceof SupportTransactions ) {
307
                ((SupportTransactions) store).setTransaction(null);
308
            }
309
            this.stores.remove(id);
310
            DisposeUtils.dispose(store);
311
//        } finally {
312
//            LOGGER.info("remove in");        
313
//        }
314
    }
315

    
316
    @Override
317
    public void remove(DataServerExplorer serverExplorer) {
318
        if( this.inProgress ) {
319
            throw new IllegalStateException("Can't remove server explorer from a in progress transaction.");
320
        }
321
        String id = null;
322
        for (Map.Entry<String, DataServerExplorer> entry : this.explorers.entrySet()) {
323
            if( serverExplorer == entry.getValue() ) {
324
                id = entry.getKey();
325
                break;
326
            }
327
        }
328
        if( id==null ) {
329
            return;
330
        }
331
        if( serverExplorer instanceof SupportTransactions ) {
332
            ((SupportTransactions) serverExplorer).setTransaction(null);
333
        }
334
        this.explorers.remove(id);
335
        DisposeUtils.dispose(serverExplorer);
336
    }
337

    
338
    @Override
339
    public boolean isInProgress() {
340
        return inProgress;
341
    }
342

    
343
    @Override
344
    public void doDispose() {
345
//        LOGGER.info("doDispose in "+this.getCode());        
346
        
347
        if( this.inProgress ) {
348
            this.rollbackQuietly();
349
        }
350
        for (Pair<DataStore, Boolean> item : stores.values()) {
351
            DataStore store = getStore(item);
352
            if( store instanceof SupportTransactions ) {
353
                ((SupportTransactions) store).setTransaction(null);
354
            }
355
            DisposeUtils.disposeQuietly(store);
356
            
357
        }
358
        for (DataServerExplorer explorer : explorers.values()) {
359
            if( explorer instanceof SupportTransactions ) {
360
                ((SupportTransactions) explorer).setTransaction(null);
361
            }
362
            DisposeUtils.disposeQuietly(explorer);
363
            
364
        }
365
        for (Disposable resource : disposables) {
366
            if( resource instanceof SupportTransactions ) {
367
                ((SupportTransactions) resource).setTransaction(null);
368
            }
369
            DisposeUtils.disposeQuietly(resource);            
370
        }
371
        for (SupportTransactions obj : supportTransactions) {
372
            obj.setTransaction(null);
373
            DisposeUtils.disposeQuietly(obj);            
374
        }
375
        for (ConnectionService connection : this.connections.values()) {
376
            connection.dispose();
377
        }
378
        this.supportTransactions = null;
379
        this.disposables = null;
380
        this.stores = null;
381
//        LOGGER.info("doDispose out");        
382
   }
383

    
384
    @Override
385
    public void close() throws Exception {
386
        this.dispose();
387
    }
388

    
389
    @Override
390
    public void addConnection(ConnectionService connection) {
391
        if( this.connections.containsKey(connection.getId()) ) {
392
            return;
393
        }
394
        this.connections.put(connection.getId(), connection);
395
    }
396

    
397
    @Override
398
    public ConnectionService getConnection(String id) {
399
        return this.connections.get(id);
400
    }
401

    
402
    @Override
403
    public void removeConnection(String id) {
404
        this.connections.remove(id);
405
    }
406

    
407
    @Override
408
    public boolean existsConnection(String id) {
409
        return this.connections.containsKey(id);
410
    }
411

    
412
    @Override
413
    public void addObserver(Observer obsrvr) {
414
        this.observableHelper.addObserver(obsrvr);
415
    }
416

    
417
    @Override
418
    public void deleteObserver(Observer obsrvr) {
419
        this.observableHelper.deleteObserver(obsrvr);
420
    }
421

    
422
    @Override
423
    public void deleteObservers() {
424
        this.observableHelper.deleteObservers();
425
    }
426

    
427
    @Override
428
    public boolean contains(DataServerExplorer explorer) {
429
        for (DataServerExplorer value : this.explorers.values()) {
430
            if(explorer == value){
431
                return true;
432
            }
433
        }
434
        return false;
435
    }
436

    
437
    @Override
438
    public boolean contains(DataStore store) {
439
        for (Pair<DataStore, Boolean> item : stores.values()) {
440
            DataStore value = getStore(item);
441
            if(store == value){
442
                return true;
443
            }
444
        }
445
        return false;
446
    }
447
    
448
    private DataStore getStore(Pair<DataStore, Boolean> item) {
449
        if( item == null ) {
450
            return null;
451
        }
452
        return item.getLeft();
453
    }
454
        
455
}