View Javadoc

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  	// private TreeMap<String, Entry> modifiedEntries = new TreeMap<String,
47  	// Entry>();
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 			// ignore the first token. This is the word "transactions".
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 		// if (!this.modifiedEntries.contains(e)) {
152 		// this.modifiedEntries.add(e);
153 		// }
154 		// this.modifiedEntries.put(e.getId(), e);
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 		// return this.modifiedEntries;
175 		// return new ArrayList<Entry>(this.modifiedEntries.values());
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 		// OPTIMIZE Can we avoid the copy?
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 		// FIXME: ist this enough? do we have to compare other values?
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 	 * 	&ltprotocol&gt://&lthost&gt:&ltport&gt/transactions/&ltidentifier&gt
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 }