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 @ 47779
History | View | Annotate | Download (15.2 KB)
1 | 45445 | jjdelcerro | /*
|
---|---|---|---|
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 | 47673 | jjdelcerro | import java.util.Collection; |
27 | 45650 | jjdelcerro | import java.util.HashMap; |
28 | 47606 | fdiaz | import java.util.HashSet; |
29 | 45445 | jjdelcerro | import java.util.List; |
30 | 45650 | jjdelcerro | import java.util.Map; |
31 | 47606 | fdiaz | import java.util.Set; |
32 | 45445 | jjdelcerro | import java.util.UUID; |
33 | 46160 | jjdelcerro | import org.apache.commons.lang3.StringUtils; |
34 | 47217 | fdiaz | import org.apache.commons.lang3.tuple.MutablePair; |
35 | import org.apache.commons.lang3.tuple.Pair; |
||
36 | 45482 | fdiaz | import org.gvsig.fmap.dal.DataServerExplorer; |
37 | 45445 | jjdelcerro | import org.gvsig.fmap.dal.DataStore; |
38 | 45650 | jjdelcerro | import org.gvsig.fmap.dal.SupportTransactions; |
39 | 45445 | jjdelcerro | import org.gvsig.fmap.dal.exception.DataException; |
40 | import org.gvsig.fmap.dal.feature.FeatureStore; |
||
41 | 45482 | fdiaz | import static org.gvsig.fmap.dal.feature.FeatureStore.MODE_QUERY; |
42 | 45650 | jjdelcerro | import org.gvsig.fmap.dal.spi.DataTransactionServices; |
43 | 45445 | jjdelcerro | import org.gvsig.tools.dispose.Disposable; |
44 | import org.gvsig.tools.dispose.DisposeUtils; |
||
45 | 47188 | fdiaz | import org.gvsig.tools.dispose.impl.AbstractDisposable; |
46 | 46315 | jjdelcerro | import org.gvsig.tools.observer.BaseNotification; |
47 | import org.gvsig.tools.observer.ObservableHelper; |
||
48 | import org.gvsig.tools.observer.Observer; |
||
49 | 46230 | jjdelcerro | import org.slf4j.Logger; |
50 | import org.slf4j.LoggerFactory; |
||
51 | 45445 | jjdelcerro | |
52 | /**
|
||
53 | *
|
||
54 | * @author gvSIG Team
|
||
55 | */
|
||
56 | @SuppressWarnings("UseSpecificCatch") |
||
57 | 47188 | fdiaz | public class DefaultTransaction extends AbstractDisposable implements DataTransactionServices { |
58 | 45445 | jjdelcerro | |
59 | 46230 | jjdelcerro | private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTransaction.class); |
60 | |||
61 | 45445 | jjdelcerro | private final String code; |
62 | 46160 | jjdelcerro | private final Map<String,DataServerExplorer> explorers; |
63 | 45650 | jjdelcerro | private final Map<String,ConnectionService> connections; |
64 | 47217 | fdiaz | private Map<String,Pair<DataStore,Boolean>> stores; |
65 | 45445 | jjdelcerro | private boolean inProgress; |
66 | private List<Disposable> disposables; |
||
67 | 46315 | jjdelcerro | private ObservableHelper observableHelper;
|
68 | 47606 | fdiaz | private Set<SupportTransactions> supportTransactions; |
69 | 45445 | jjdelcerro | |
70 | public DefaultTransaction() {
|
||
71 | this.code = UUID.randomUUID().toString().replace("-", ""); |
||
72 | 46160 | jjdelcerro | this.stores = new HashMap<>(); |
73 | this.explorers = new HashMap<>(); |
||
74 | 45445 | jjdelcerro | this.disposables = new ArrayList<>(); |
75 | 47606 | fdiaz | this.supportTransactions = new HashSet<>(); |
76 | 45445 | jjdelcerro | this.inProgress = false; |
77 | 45650 | jjdelcerro | this.connections = new HashMap<>(); |
78 | 46315 | jjdelcerro | this.observableHelper = new ObservableHelper(); |
79 | 45445 | jjdelcerro | } |
80 | |||
81 | @Override
|
||
82 | public String getCode() { |
||
83 | return this.code; |
||
84 | } |
||
85 | |||
86 | @Override
|
||
87 | public void begin() throws DataException { |
||
88 | 45650 | jjdelcerro | if( this.inProgress ) { |
89 | throw new IllegalStateException("Transaction already started."); |
||
90 | } |
||
91 | 46315 | jjdelcerro | this.observableHelper.notifyObservers(this, new BaseNotification("BEGIN", null)); |
92 | 45445 | jjdelcerro | this.inProgress = true; |
93 | } |
||
94 | |||
95 | @Override
|
||
96 | public void commit() throws DataException { |
||
97 | 47673 | jjdelcerro | if (!this.isInProgress()) { |
98 | 46125 | fdiaz | throw new IllegalStateException("Can't commit transaction without begin."); |
99 | 45445 | jjdelcerro | } |
100 | 47673 | jjdelcerro | 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 | 45445 | jjdelcerro | } |
118 | 47673 | jjdelcerro | if( !needretry ) {
|
119 | break;
|
||
120 | } |
||
121 | LOGGER.debug("commit: retry store finish editing");
|
||
122 | retries--; |
||
123 | 45445 | jjdelcerro | } |
124 | 47673 | jjdelcerro | 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 | 45445 | jjdelcerro | } |
132 | } |
||
133 | |||
134 | @Override
|
||
135 | public void rollback() throws DataException { |
||
136 | if( !this.isInProgress() ) { |
||
137 | 46125 | fdiaz | throw new IllegalStateException("Can't rollback transaction without begin."); |
138 | 45445 | jjdelcerro | } |
139 | 47673 | jjdelcerro | // LOGGER.info("rollback in");
|
140 | 47217 | fdiaz | for (Pair<DataStore, Boolean> item : stores.values()) { |
141 | 47328 | jjdelcerro | DataStore store = getStore(item); |
142 | 45445 | jjdelcerro | if( store instanceof FeatureStore ) { |
143 | FeatureStore fstore = (FeatureStore) store; |
||
144 | 45482 | fdiaz | if( fstore.getMode() != MODE_QUERY) {
|
145 | 45445 | jjdelcerro | fstore.cancelEditing(); |
146 | } |
||
147 | } |
||
148 | } |
||
149 | 46315 | jjdelcerro | for (ConnectionService connection : this.connections.values()) { |
150 | connection.abort(); |
||
151 | } |
||
152 | this.observableHelper.notifyObservers(this, new BaseNotification("ROLLBACK", null)); |
||
153 | 45445 | jjdelcerro | this.inProgress = false; |
154 | 47673 | jjdelcerro | // LOGGER.info("rollback out");
|
155 | 45445 | jjdelcerro | } |
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 | 46160 | jjdelcerro | add(store, null, true); |
169 | 45482 | fdiaz | } |
170 | |||
171 | @Override
|
||
172 | 46160 | jjdelcerro | public void add(DataStore store, String id) { |
173 | this.add(store, id, true); |
||
174 | } |
||
175 | |||
176 | @Override
|
||
177 | 45482 | fdiaz | public void add(DataStore store, boolean local) { |
178 | 46160 | jjdelcerro | this.add(store, null, local); |
179 | } |
||
180 | |||
181 | @Override
|
||
182 | public void add(DataStore store, String id, boolean local) { |
||
183 | 47673 | jjdelcerro | if (store == null) { |
184 | 47606 | fdiaz | throw new IllegalArgumentException("The store is required."); |
185 | } |
||
186 | 47673 | jjdelcerro | // 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 | 46160 | jjdelcerro | } |
199 | } |
||
200 | 47673 | jjdelcerro | 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 | 45445 | jjdelcerro | } |
211 | |||
212 | @Override
|
||
213 | 46160 | jjdelcerro | public FeatureStore getFeatureStore(String id) { |
214 | 47779 | fdiaz | FeatureStore store = (FeatureStore) this.getStore(this.stores.get(id)); |
215 | if(store == null){ |
||
216 | for (Pair<DataStore, Boolean> value : stores.values()) { |
||
217 | DataStore currentStore = this.getStore(value);
|
||
218 | if(StringUtils.equalsIgnoreCase(id, currentStore.getName())){
|
||
219 | if(store != null){ |
||
220 | if(!StringUtils.equals(store.getFullName(), currentStore.getFullName())){
|
||
221 | return null; |
||
222 | } |
||
223 | } |
||
224 | store = (FeatureStore) this.getStore(value);
|
||
225 | } |
||
226 | } |
||
227 | } |
||
228 | return store;
|
||
229 | 46160 | jjdelcerro | } |
230 | |||
231 | @Override
|
||
232 | 45482 | fdiaz | public void add(DataServerExplorer explorer) { |
233 | 46160 | jjdelcerro | this.add(explorer, null, true); |
234 | 45482 | fdiaz | } |
235 | |||
236 | @Override
|
||
237 | 46160 | jjdelcerro | public void add(DataServerExplorer explorer, String id) { |
238 | this.add(explorer, id, true); |
||
239 | } |
||
240 | |||
241 | @Override
|
||
242 | 45482 | fdiaz | public void add(DataServerExplorer explorer, boolean local) { |
243 | 46160 | jjdelcerro | this.add(explorer, null, local); |
244 | } |
||
245 | |||
246 | @Override
|
||
247 | public void add(DataServerExplorer explorer, String id, boolean local) { |
||
248 | 47606 | fdiaz | if(explorer == null){ |
249 | throw new IllegalArgumentException("The explorer is required."); |
||
250 | } |
||
251 | 46160 | jjdelcerro | String theId = id;
|
252 | if( StringUtils.isBlank(id) ) {
|
||
253 | theId = String.valueOf(explorer.hashCode());
|
||
254 | } else {
|
||
255 | DataServerExplorer theExplorer = this.explorers.get(theId);
|
||
256 | if(theExplorer!=null ){ |
||
257 | if( theExplorer==explorer ) {
|
||
258 | return;
|
||
259 | } |
||
260 | throw new IllegalArgumentException("The id '"+id+"' is already used."); |
||
261 | } |
||
262 | 45482 | fdiaz | } |
263 | 46160 | jjdelcerro | |
264 | 45650 | jjdelcerro | if( explorer instanceof SupportTransactions ) { |
265 | ((SupportTransactions) explorer).setTransaction(this);
|
||
266 | } |
||
267 | 45482 | fdiaz | if(!local){
|
268 | DisposeUtils.bind(explorer); |
||
269 | } |
||
270 | 46160 | jjdelcerro | this.explorers.put(theId,explorer);
|
271 | 45482 | fdiaz | } |
272 | 46160 | jjdelcerro | |
273 | @Override
|
||
274 | public DataServerExplorer getServerExplorer(String id) { |
||
275 | return this.explorers.get(id); |
||
276 | } |
||
277 | 45482 | fdiaz | |
278 | @Override
|
||
279 | 45445 | jjdelcerro | public void add(Disposable resource) throws DataException { |
280 | this.disposables.add(resource);
|
||
281 | } |
||
282 | |||
283 | @Override
|
||
284 | 47606 | fdiaz | public void add(SupportTransactions obj, boolean local) throws DataException { |
285 | if(obj == null){ |
||
286 | throw new IllegalArgumentException("The transaction supplier is required."); |
||
287 | } |
||
288 | if(obj instanceof DataStore){ |
||
289 | this.add((DataStore)obj, local);
|
||
290 | return;
|
||
291 | } |
||
292 | if(obj instanceof DataServerExplorer){ |
||
293 | this.add((DataServerExplorer)obj, local);
|
||
294 | return;
|
||
295 | } |
||
296 | obj.setTransaction(this);
|
||
297 | if(!local){
|
||
298 | DisposeUtils.bind(obj); |
||
299 | } |
||
300 | this.supportTransactions.add(obj);
|
||
301 | } |
||
302 | |||
303 | @Override
|
||
304 | 45445 | jjdelcerro | public void remove(DataStore store) { |
305 | 47217 | fdiaz | if( this.inProgress && !DisposeUtils.isNullOrDisposed(store)){ |
306 | 45445 | jjdelcerro | throw new IllegalStateException("Can't remove store from a in progress transaction."); |
307 | } |
||
308 | 47673 | jjdelcerro | // try {
|
309 | // LOGGER.info("remove in");
|
||
310 | String id = null; |
||
311 | for (Map.Entry<String, Pair<DataStore,Boolean>> entry : this.stores.entrySet()) { |
||
312 | if( store == getStore(entry.getValue()) ) {
|
||
313 | id = entry.getKey(); |
||
314 | break;
|
||
315 | } |
||
316 | 46160 | jjdelcerro | } |
317 | 47673 | jjdelcerro | if( id==null ) { |
318 | return;
|
||
319 | } |
||
320 | if( store instanceof SupportTransactions ) { |
||
321 | ((SupportTransactions) store).setTransaction(null);
|
||
322 | } |
||
323 | this.stores.remove(id);
|
||
324 | DisposeUtils.dispose(store); |
||
325 | // } finally {
|
||
326 | // LOGGER.info("remove in");
|
||
327 | // }
|
||
328 | 45445 | jjdelcerro | } |
329 | |||
330 | @Override
|
||
331 | 46160 | jjdelcerro | public void remove(DataServerExplorer serverExplorer) { |
332 | if( this.inProgress ) { |
||
333 | throw new IllegalStateException("Can't remove server explorer from a in progress transaction."); |
||
334 | } |
||
335 | String id = null; |
||
336 | for (Map.Entry<String, DataServerExplorer> entry : this.explorers.entrySet()) { |
||
337 | if( serverExplorer == entry.getValue() ) {
|
||
338 | id = entry.getKey(); |
||
339 | break;
|
||
340 | } |
||
341 | } |
||
342 | if( id==null ) { |
||
343 | return;
|
||
344 | } |
||
345 | if( serverExplorer instanceof SupportTransactions ) { |
||
346 | ((SupportTransactions) serverExplorer).setTransaction(null);
|
||
347 | } |
||
348 | this.explorers.remove(id);
|
||
349 | DisposeUtils.dispose(serverExplorer); |
||
350 | } |
||
351 | |||
352 | @Override
|
||
353 | 45445 | jjdelcerro | public boolean isInProgress() { |
354 | return inProgress;
|
||
355 | } |
||
356 | |||
357 | @Override
|
||
358 | 47188 | fdiaz | public void doDispose() { |
359 | 47673 | jjdelcerro | // LOGGER.info("doDispose in "+this.getCode());
|
360 | |||
361 | 45445 | jjdelcerro | if( this.inProgress ) { |
362 | this.rollbackQuietly();
|
||
363 | } |
||
364 | 47217 | fdiaz | for (Pair<DataStore, Boolean> item : stores.values()) { |
365 | 47328 | jjdelcerro | DataStore store = getStore(item); |
366 | 45650 | jjdelcerro | if( store instanceof SupportTransactions ) { |
367 | ((SupportTransactions) store).setTransaction(null);
|
||
368 | } |
||
369 | 45445 | jjdelcerro | DisposeUtils.disposeQuietly(store); |
370 | |||
371 | } |
||
372 | 46160 | jjdelcerro | for (DataServerExplorer explorer : explorers.values()) {
|
373 | 45650 | jjdelcerro | if( explorer instanceof SupportTransactions ) { |
374 | ((SupportTransactions) explorer).setTransaction(null);
|
||
375 | } |
||
376 | 45482 | fdiaz | DisposeUtils.disposeQuietly(explorer); |
377 | |||
378 | } |
||
379 | 45445 | jjdelcerro | for (Disposable resource : disposables) {
|
380 | 45650 | jjdelcerro | if( resource instanceof SupportTransactions ) { |
381 | ((SupportTransactions) resource).setTransaction(null);
|
||
382 | } |
||
383 | 46315 | jjdelcerro | DisposeUtils.disposeQuietly(resource); |
384 | 45445 | jjdelcerro | } |
385 | 47606 | fdiaz | for (SupportTransactions obj : supportTransactions) {
|
386 | obj.setTransaction(null);
|
||
387 | DisposeUtils.disposeQuietly(obj); |
||
388 | } |
||
389 | 46315 | jjdelcerro | for (ConnectionService connection : this.connections.values()) { |
390 | connection.dispose(); |
||
391 | } |
||
392 | 47606 | fdiaz | this.supportTransactions = null; |
393 | 45445 | jjdelcerro | this.disposables = null; |
394 | this.stores = null; |
||
395 | 47673 | jjdelcerro | // LOGGER.info("doDispose out");
|
396 | } |
||
397 | 45445 | jjdelcerro | |
398 | @Override
|
||
399 | public void close() throws Exception { |
||
400 | this.dispose();
|
||
401 | } |
||
402 | 45650 | jjdelcerro | |
403 | @Override
|
||
404 | public void addConnection(ConnectionService connection) { |
||
405 | if( this.connections.containsKey(connection.getId()) ) { |
||
406 | return;
|
||
407 | } |
||
408 | this.connections.put(connection.getId(), connection);
|
||
409 | } |
||
410 | |||
411 | @Override
|
||
412 | public ConnectionService getConnection(String id) { |
||
413 | return this.connections.get(id); |
||
414 | } |
||
415 | |||
416 | @Override
|
||
417 | public void removeConnection(String id) { |
||
418 | this.connections.remove(id);
|
||
419 | } |
||
420 | |||
421 | @Override
|
||
422 | public boolean existsConnection(String id) { |
||
423 | return this.connections.containsKey(id); |
||
424 | } |
||
425 | 46315 | jjdelcerro | |
426 | @Override
|
||
427 | public void addObserver(Observer obsrvr) { |
||
428 | this.observableHelper.addObserver(obsrvr);
|
||
429 | } |
||
430 | |||
431 | @Override
|
||
432 | public void deleteObserver(Observer obsrvr) { |
||
433 | this.observableHelper.deleteObserver(obsrvr);
|
||
434 | } |
||
435 | |||
436 | @Override
|
||
437 | public void deleteObservers() { |
||
438 | this.observableHelper.deleteObservers();
|
||
439 | } |
||
440 | 46316 | fdiaz | |
441 | @Override
|
||
442 | public boolean contains(DataServerExplorer explorer) { |
||
443 | for (DataServerExplorer value : this.explorers.values()) { |
||
444 | if(explorer == value){
|
||
445 | return true; |
||
446 | } |
||
447 | } |
||
448 | return false; |
||
449 | } |
||
450 | |||
451 | @Override
|
||
452 | public boolean contains(DataStore store) { |
||
453 | 47217 | fdiaz | for (Pair<DataStore, Boolean> item : stores.values()) { |
454 | 47328 | jjdelcerro | DataStore value = getStore(item); |
455 | 46316 | fdiaz | if(store == value){
|
456 | return true; |
||
457 | } |
||
458 | } |
||
459 | return false; |
||
460 | } |
||
461 | 47328 | jjdelcerro | |
462 | private DataStore getStore(Pair<DataStore, Boolean> item) { |
||
463 | if( item == null ) { |
||
464 | return null; |
||
465 | } |
||
466 | return item.getLeft();
|
||
467 | } |
||
468 | |||
469 | 45445 | jjdelcerro | } |