1 package org.xvsm.transactions;
2
3 import java.io.Serializable;
4 import java.net.URI;
5 import java.net.URISyntaxException;
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.StringTokenizer;
9 import java.util.TreeMap;
10
11 import org.xvsm.configuration.ConfigurationManager;
12 import org.xvsm.core.ContainerRef;
13 import org.xvsm.core.Entry;
14 import org.xvsm.interfaces.ICapi;
15 import org.xvsm.interfaces.ICoordinator;
16 import org.xvsm.internal.exceptions.FatalException;
17 import org.xvsm.remote.TransportHandler;
18
19 /***
20 *
21 * @author Christian Schreiber, Michael Proestler
22 *
23 */
24 public class Transaction implements Serializable {
25 /***
26 * Auto generated serial version uid.
27 */
28 private static final long serialVersionUID = -2895233282572881371L;
29
30 /***
31 * An unique identifier for this transaction.
32 */
33 private String id;
34
35 private boolean underRollback = false;
36
37 /***
38 * indicates whether this transaction is an implicit transaction.
39 */
40 private boolean implicit = false;
41
42 /***
43 * A {@link List} of {@link Entry}s which have been modified under this
44 * {@link Transaction}.
45 */
46
47
48 private transient TreeMap<ContainerRef, TreeMap<String, Entry>> modifiedEntries = new TreeMap<ContainerRef, TreeMap<String, Entry>>();
49
50 /***
51 * The remaining timeout of this transaction.
52 */
53 private long timeout;
54
55 /***
56 * The time when the timeout has been updated.
57 */
58 private long lastTimeoutUpdate;
59
60 /***
61 * A {@link List} of {@link ICoordinator}s which have been used under this
62 * {@link Transaction}.
63 */
64 private transient List<ICoordinator> modifiedCoordinators = new ArrayList<ICoordinator>();
65
66 /***
67 * A {@link List} of {@link ContainerRef}s which have been used under this
68 * {@link Transaction}.
69 */
70 private transient List<ContainerRef> modifiedContainers = new ArrayList<ContainerRef>();
71
72 /***
73 * The father transaction.
74 */
75 private Transaction father = null;
76
77 /***
78 * The site on which this transaction has been created.
79 */
80 private URI site = null;
81
82
83 /***
84 * Create a new Transaction.
85 *
86 * @param id
87 * the unique identifier for the new transaction.
88 */
89 public Transaction(String id) {
90 this.id = id;
91 this.lastTimeoutUpdate = System.currentTimeMillis();
92 }
93
94 /***
95 * Create a new Transaction.
96 *
97 * @param id
98 * the unique identifier for the new transaction.
99 * @param father
100 * the father of this transaction.
101 */
102 public Transaction(String id, Transaction father) {
103 this(id);
104 this.father = father;
105 }
106
107 /***
108 * Creates a new transaction.
109 *
110 * @param uri
111 * the URI which represents the transaction. This URI has to have
112 * the same format as URIs returned with {@link #asURI()}.
113 */
114 public Transaction(URI uri) {
115 try {
116 this.site = new URI(uri.getScheme() + "://" + uri.getAuthority());
117 String path = uri.getPath();
118 StringTokenizer st = new StringTokenizer(path, "/");
119
120 st.nextToken();
121 this.id = st.nextToken();
122 } catch (URISyntaxException e) {
123 throw new FatalException(e);
124 }
125 }
126
127 /***
128 * Create a new Transaction.
129 *
130 * @param id
131 * the unique identifier for the new transaction.
132 * @param implicit
133 * indicates whether this transaction is an implicit transaction.
134 */
135 public Transaction(String id, boolean implicit) {
136 this(id);
137 this.implicit = implicit;
138 }
139
140 /***
141 * Adds an {@link Entry} to the list of modified Entries if it has not
142 * already been added.
143 *
144 * @param e
145 * the Entry to add.
146 * @param cref
147 * the ContainerRef of the container where the entry was
148 * modified.
149 */
150 public void addEntry(ContainerRef cref, Entry e) {
151
152
153
154
155 TreeMap<String, Entry> map = this.modifiedEntries.get(cref);
156 if (map == null) {
157 map = new TreeMap<String, Entry>();
158 this.modifiedEntries.put(cref, map);
159 }
160 map.put(e.getId(), e);
161 }
162
163 /***
164 * Returns an Array with all Entries which have been modified under this
165 * Transaction in the Container with the given ContainerRef.
166 *
167 * @param cref
168 * the ContainerRef of the Container from which the modified
169 * entries should be returned.
170 *
171 * @return the Array with the Entries.
172 */
173 public List<Entry> getModifiedEntries(ContainerRef cref) {
174
175
176 TreeMap<String, Entry> map = this.modifiedEntries.get(cref);
177 if (map == null) {
178 return new ArrayList<Entry>();
179 } else {
180 return new ArrayList<Entry>(map.values());
181 }
182 }
183
184 /***
185 * Removes an Entry from the modified List of this Transaction.
186 *
187 * @param cref
188 * the ContainerRef of the Container were the Entry should be
189 * removed from the modified list.
190 * @param e
191 * the Entry that should be removed.
192 */
193 public void removeEntry(ContainerRef cref, Entry e) {
194 TreeMap<String, Entry> map = this.modifiedEntries.get(cref);
195 if (map == null) {
196 return;
197 }
198 map.remove(e.getId());
199 }
200
201 /***
202 * Add the Coordinator to the list of coordinators that have been used with
203 * this transaction.
204 *
205 * @param c
206 * the new coordinator.
207 */
208 public void addCoordinator(ICoordinator c) {
209 if (!this.modifiedCoordinators.contains(c)) {
210 this.modifiedCoordinators.add(c);
211 }
212 }
213
214 /***
215 * Get the coordinators of this Transaction.
216 *
217 * @return the modifiedCoordinators
218 */
219 public List<ICoordinator> getModifiedCoordinators() {
220 return modifiedCoordinators;
221 }
222
223 /***
224 * Delete the coordinator from this Transaction.
225 *
226 * @param coordinator
227 * the coordinator to remove.
228 */
229 public void removeCoordinator(ICoordinator coordinator) {
230 this.modifiedCoordinators.remove(coordinator);
231 }
232
233 /***
234 * Removes the cref from this transaction.
235 *
236 * @param cref
237 * the cref to remove.
238 */
239 public void removeContainerRef(ContainerRef cref) {
240 this.modifiedContainers.remove(cref);
241 }
242
243 /***
244 * Adds a cref to the list of modified containers.
245 *
246 * @param cref
247 * the cref to add.
248 */
249 public void addContainerRef(ContainerRef cref) {
250 if (!this.modifiedContainers.contains(cref)) {
251 this.modifiedContainers.add(cref);
252 }
253 }
254
255 /***
256 * Returs the list of modified containers.
257 *
258 * @return the containerRefs.
259 */
260 public List<ContainerRef> getModifiedContainers() {
261
262 return new ArrayList<ContainerRef>(this.modifiedContainers);
263 }
264
265 /***
266 * Get the Identifier of this Transaction.
267 *
268 * @return the id
269 */
270 public String getId() {
271 return id;
272 }
273
274 /***
275 * Get the boolean of this Transaction.
276 *
277 * @return the implicit
278 */
279 public boolean isImplicit() {
280 return implicit;
281 }
282
283 /***
284 * Set the boolean of this Transaction.
285 *
286 * @param implicit
287 * the implicit to set
288 */
289 public void setImplicit(boolean implicit) {
290 this.implicit = implicit;
291 }
292
293 /***
294 * Get the father of this Transaction.
295 *
296 * @return the father
297 */
298 public Transaction getFather() {
299 return father;
300 }
301
302 /***
303 * Set the father of this Transaction.
304 *
305 * @param father
306 * the father to set
307 */
308 public void setFather(Transaction father) {
309 this.father = father;
310 }
311
312 /***
313 * Determines whether this transaction can be used with tx. The method
314 * returns true if this transaction is equals tx or tx is an ancestor of
315 * this.
316 *
317 * @param tx
318 * the transaction to check.
319 * @return <code>true</code> if tx is equals to this or tx is an ancestor
320 * of this. Otherwise <code>false</code>
321 */
322 public boolean canUse(Transaction tx) {
323 if (this.equals(tx)) {
324 return true;
325 }
326 if (this.father != null) {
327 return this.father.canUse(tx);
328 }
329 return false;
330 }
331
332 /***
333 * Determines if this Transaction is an ancestor of the given on.
334 *
335 * @param tx
336 * the transaction to determine wether this transaction is an
337 * ancestor of it or not.
338 * @return true if it is an ancestor or it is the same transaction. false
339 * otherwise.
340 */
341 public boolean isAncestorOf(Transaction tx) {
342 boolean ret = this.equals(tx);
343 if (!ret && tx.getFather() != null) {
344 return this.isAncestorOf(tx.getFather());
345 }
346 return ret;
347 }
348
349 /***
350 * Get the timeout of this Transaction.
351 *
352 * @return the timeout
353 */
354 public long getTimeout() {
355 return timeout;
356 }
357
358 /***
359 * Set the timeout of this Transaction.
360 *
361 * @param timeout
362 * the timeout to set
363 */
364 public void setTimeout(long timeout) {
365 this.timeout = timeout;
366 }
367
368 /***
369 * Updates the timeout of this transaction.
370 *
371 */
372 public void updateTimeout() {
373 if (this.timeout != ICapi.INFINITE_TIMEOUT) {
374 long now = System.currentTimeMillis();
375 this.timeout -= now - this.lastTimeoutUpdate;
376 this.lastTimeoutUpdate = now;
377 }
378 }
379
380 /***
381 * Returns <code>true</code> if the timeout of this transactoin has been
382 * expired.
383 *
384 * @return <code>true</code> if the timeout of this transactoin has been
385 * expired
386 */
387 public boolean timeoutExpired() {
388 return this.timeout != ICapi.INFINITE_TIMEOUT && this.timeout <= 0;
389 }
390
391 /***
392 *
393 * {@inheritDoc}.
394 */
395 @Override
396 public String toString() {
397 String s = "Transaction: " + this.id + " implicit: " + this.implicit;
398 if (this.father != null) {
399 Transaction tx = this.father;
400 while (tx.getFather() != null) {
401 tx = tx.getFather();
402 }
403 s += " Origin: " + tx.getId();
404 }
405 return s;
406 }
407
408 /***
409 * {@inheritDoc}.
410 */
411 @Override
412 public int hashCode() {
413 final int prime = 31;
414 int result = 1;
415 result = prime * result + ((id == null) ? 0 : id.hashCode());
416 return result;
417 }
418
419 /***
420 * {@inheritDoc}.
421 */
422 @Override
423 public boolean equals(Object obj) {
424
425 return (obj instanceof Transaction)
426 && (this.id.equals(((Transaction) obj).getId()));
427 }
428
429 /***
430 * @return the site
431 */
432 public URI getSite() {
433 return site;
434 }
435
436 /***
437 * Returns the URI representing this transaction. <br>
438 * Format: <code>
439 * <protocol>://<host>:<port>/transactions/<identifier>
440 * </code>
441 *
442 * @return the uri representation of this transaction
443 */
444 public URI asURI() {
445 try {
446 if (this.getSite() != null) {
447 return new URI(this.getSite().toASCIIString()
448 + "/transactions/" + this.id);
449 } else {
450 URI uri = TransportHandler.getInstance().getListener(
451 ConfigurationManager.getInstance().getStringSetting(
452 "DefaultAnswerToProtocol")).getUri();
453 return new URI(uri.toASCIIString() + "/transactions/" + this.id);
454 }
455 } catch (URISyntaxException e) {
456 throw new FatalException(e);
457 }
458 }
459
460 /***
461 * @param site
462 * the site to set
463 */
464 public void setSite(URI site) {
465 this.site = site;
466 }
467
468 public boolean isUnderRollback() {
469 return underRollback;
470 }
471
472 public void setUnderRollback(boolean underRollback) {
473 this.underRollback = underRollback;
474 }
475 }