View Javadoc

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  		// @Override
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 		// Start one timeouthandler for the transactions.
112 		// TODO we need some logic to start more then one timeouthandler if
113 		// necessary.
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 					// throw new FatalException(e);
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 }