1 package org.xvsm.transactions;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.UUID;
9 import java.util.concurrent.ConcurrentHashMap;
10 import java.util.concurrent.ScheduledFuture;
11
12 import org.apache.log4j.Logger;
13 import org.xvsm.core.ContainerRef;
14 import org.xvsm.interfaces.container.IContainer;
15 import org.xvsm.internal.ContainerManager;
16 import org.xvsm.internal.EventProcessingPool;
17 import org.xvsm.internal.TimeoutSchedulerPool;
18 import org.xvsm.internal.exceptions.InvalidTransactionException;
19 import org.xvsm.internal.exceptions.XCoreException;
20 import org.xvsm.internal.tasks.TransactionTask;
21 import org.xvsm.internal.tasks.TransactionTaskType;
22
23 /***
24 *
25 * @author Christian Schreiber, Michael Proestler
26 *
27 */
28 public final class TransactionManager {
29
30 /***
31 * The Logger of this Class.
32 */
33 private static Logger logger = Logger.getLogger(TransactionManager.class);
34
35 /***
36 * The future object of the transaction manager.
37 */
38 private static ScheduledFuture<?> tmFuture;
39
40 /***
41 *
42 * @author Christian Schreiber, Michael Proestler
43 *
44 */
45 private class TimeoutHandler implements Runnable {
46
47 /***
48 * Creates a new TimeoutHandler.
49 *
50 */
51 public TimeoutHandler() {
52 }
53
54 /***
55 *
56 * {@inheritDoc}.
57 */
58
59 public void run() {
60 Thread.currentThread().setName(this.getClass().getName());
61 for (Transaction tx : knownTransaction.values()) {
62 tx.updateTimeout();
63 if (tx.timeoutExpired()) {
64 EventProcessingPool.getInstance().execute(
65 new TransactionTask(TransactionTaskType.ROLLBACK,
66 tx));
67 }
68 }
69 }
70 }
71
72 /***
73 * The map contains all known Transaction.
74 */
75 private Map<String, Transaction> knownTransaction = new ConcurrentHashMap<String, Transaction>();
76
77 /***
78 * static instance.
79 */
80 private static TransactionManager tm = null;
81
82 /***
83 * Factory method.
84 *
85 * @return a TransactionManager instance.
86 */
87 public static synchronized TransactionManager getInstance() {
88 if (tm == null) {
89 tm = new TransactionManager();
90 }
91 return tm;
92 }
93
94 /***
95 * Destroys the current TransactionManager instance.
96 *
97 */
98 public static synchronized void removeInstance() {
99 if (tmFuture != null) {
100 tmFuture.cancel(true);
101 }
102 tmFuture = null;
103 tm = null;
104 }
105
106 /***
107 * Invisible default Constructor. Utility Classes must not have a public
108 * default constructor.
109 */
110 private TransactionManager() {
111
112
113
114 TimeoutHandler th = new TimeoutHandler();
115 tmFuture = TimeoutSchedulerPool.schedule(th);
116 }
117
118 /***
119 * Creates a new Transaction.
120 *
121 * @param timeout
122 * the timeout of the transaction
123 * @return a new Transaction.
124 */
125 public Transaction createTransaction(long timeout) {
126 if (logger.isDebugEnabled()) {
127 logger.debug("createTransaction(" + timeout + ")");
128 }
129
130 Transaction tx = new Transaction(UUID.randomUUID().toString());
131 tx.setTimeout(timeout);
132 this.knownTransaction.put(tx.getId(), tx);
133
134 if (logger.isDebugEnabled()) {
135 logger.debug("createTransaction() transaction created: " + tx);
136 }
137
138 return tx;
139 }
140
141 /***
142 * Creates a new Transaction which is a child of tx.
143 *
144 * @param tx
145 * the father transaction.
146 * @return a new Transaction.
147 */
148 public Transaction createSubTransaction(Transaction tx) {
149 Transaction newtx = this.createTransaction(tx.getTimeout());
150 newtx.setFather(tx);
151 return newtx;
152 }
153
154 /***
155 * Creates a new implicit Transaction.
156 *
157 * @param timeout
158 * the timeout of the transaction
159 * @return a new Transaction.
160 */
161 public Transaction createImplicitTransaction(long timeout) {
162 Transaction tx = this.createTransaction(timeout);
163 tx.setImplicit(true);
164 return tx;
165 }
166
167 /***
168 * Removes a {@link Transaction} from the known TransactionManager. This
169 * method has to be used when a transaction is committed or rolled back.
170 *
171 * @param tx
172 * The {@link Transaction} that should be removed.
173 */
174 public void removeTransaction(Transaction tx) {
175 this.knownTransaction.remove(tx.getId());
176 }
177
178 /***
179 * Returns if the given {@link Transaction} is valid (eg. correctly created,
180 * not committed or rolled back).
181 *
182 * @param tx
183 * the transaction to check.
184 * @return <code>true</code> if tx is valid otherwise <code>false</code>
185 */
186 public boolean isValid(Transaction tx) {
187 return this.knownTransaction.containsKey(tx.getId());
188 }
189
190 /***
191 * Get the Transaction object according to the given identifier.
192 *
193 * @param id
194 * the identifier of the transaction.
195 * @throws InvalidTransactionException
196 * if there is no Transaction with this identifier.
197 * @return the matching Transaction object.
198 */
199 public Transaction getTransaction(String id)
200 throws InvalidTransactionException {
201 Transaction tx = this.knownTransaction.get(id);
202 if (tx == null) {
203 throw new InvalidTransactionException();
204 }
205 return tx;
206 }
207
208 /***
209 * Get a list of all transactions.
210 *
211 * @return a list of all transactions objects.
212 */
213 public List<Transaction> getAll() {
214 return new ArrayList<Transaction>(this.knownTransaction.values());
215 }
216
217 /***
218 * Commits a Transaction.
219 *
220 * @param tx
221 * the Transaction that should be commited.
222 * @throws XCoreException
223 * if something went wrong during adding the aspect. see
224 * .getCause() for the reason
225 */
226 public void commitTransaction(Transaction tx) throws XCoreException {
227 if (logger.isDebugEnabled()) {
228 logger.debug("commitTransaction(" + tx + ")");
229 }
230
231 IContainer cc = ContainerManager.getInstance().getContainerContainer();
232 boolean cctoCommit = false;
233 for (ContainerRef cref : tx.getModifiedContainers()) {
234 IContainer c = ContainerManager.getInstance()
235 .getContainer(tx, cref);
236 if (c != null && c.equals(cc)) {
237 cctoCommit = true;
238 } else {
239 try {
240 c.commit(tx);
241 } catch (NullPointerException e) {
242 logger.warn("Container " + cref
243 + ": Commit failed because it doesn't exists.");
244
245 }
246 }
247 }
248 if (cctoCommit) {
249 cc.commit(tx);
250 }
251
252 this.removeTransaction(tx);
253
254 if (logger.isDebugEnabled()) {
255 logger.debug("commitTransaction() transaction commited");
256 }
257 }
258
259 /***
260 * Does a rollback of the given Transaction.
261 *
262 * @param tx
263 * the Transaction that should be rollbacked.
264 * @throws XCoreException
265 * if something went wrong during adding the aspect. see
266 * .getCause() for the reason
267 */
268 public void rollbackTransaction(Transaction tx) throws XCoreException {
269 if (logger.isDebugEnabled()) {
270 logger.debug("rollbackTransaction(" + tx + ")");
271 }
272 tx.setUnderRollback(true);
273 IContainer cc = ContainerManager.getInstance().getContainerContainer();
274 boolean cctoRollback = false;
275 for (ContainerRef cref : new ArrayList<ContainerRef>(tx
276 .getModifiedContainers())) {
277 IContainer c = ContainerManager.getInstance()
278 .getContainer(tx, cref);
279 if (c.equals(cc)) {
280 cctoRollback = true;
281 } else {
282 try {
283 c.rollback(tx);
284 } catch (NullPointerException e) {
285 logger.warn("Container " + cref
286 + ": Commit failed because it doesn't exists.");
287 }
288 }
289 }
290 if (cctoRollback) {
291 cc.rollback(tx);
292 }
293
294 this.removeTransaction(tx);
295
296 if (logger.isDebugEnabled()) {
297 logger.debug("rollbackTransaction() transaction rolled back");
298 }
299
300 }
301 }