Mercurial > hg > blitz_condensed
comparison src/org/dancres/blitz/txn/TxnState.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 package org.dancres.blitz.txn; | |
2 | |
3 import java.io.IOException; | |
4 import java.util.ArrayList; | |
5 import java.util.Iterator; | |
6 import java.util.logging.Level; | |
7 | |
8 import net.jini.core.transaction.TransactionException; | |
9 import net.jini.core.transaction.UnknownTransactionException; | |
10 import net.jini.core.transaction.server.TransactionConstants; | |
11 | |
12 import org.dancres.blitz.disk.DiskTxn; | |
13 import org.dancres.blitz.notify.EventQueue; | |
14 import org.dancres.blitz.notify.QueueEvent; | |
15 import org.dancres.blitz.task.Task; | |
16 import org.dancres.blitz.task.Tasks; | |
17 | |
18 /* | |
19 * We only need to post QueueEvent.TRANSACTION_ENDED during live work, not | |
20 * during recovery as it will only be relevant to active notifies. When | |
21 * we restart the only active notifies left would be those not interested in | |
22 * the event because they were registered with a null transaction. | |
23 * | |
24 * Given the above, we can post the event from doFinalize which will be | |
25 * called in TxnManager as part of "live" operations. This neatly ensures | |
26 * we will only do this work outside of recovery. | |
27 * | |
28 * We will post TRANSACTION_ENDED even for null transactions and for each | |
29 * such event scan the generators and kill any that are anchored against | |
30 * the transaction - this should be done via a new method to test this. | |
31 * | |
32 * EventGenerators should only profess interest if they aren't already tainted | |
33 * as they do with canSee etc. | |
34 * | |
35 * Note we needn't call kill we could just pass the event into all generators | |
36 * and have them taint and then kill themselves (assuming they no longer | |
37 * dispatch CleanTask). We could then strip out TxnNotify's but we would need | |
38 * to add support for TRANSACTION_ENDED to each canSee method. | |
39 */ | |
40 | |
41 /** | |
42 Contains all state associated with a particular transaction identified | |
43 by TxnId. | |
44 | |
45 @todo Deal with TRANSACTION_ENDED etc as above | |
46 */ | |
47 public class TxnState implements java.io.Serializable { | |
48 static final long serialVersionUID = 2855252304868267667L; | |
49 | |
50 private int theState = TransactionConstants.ACTIVE; | |
51 | |
52 private TxnId theId; | |
53 | |
54 private boolean nonDestructive; | |
55 | |
56 /* | |
57 Typical transaction might be take/write or a few ops more, so we | |
58 allocate accordingly | |
59 | |
60 @todo Decide if this is appropriate/worth doing | |
61 */ | |
62 private ArrayList theOperations = new ArrayList(5); | |
63 | |
64 TxnState(TxnId anId) { | |
65 theId = anId; | |
66 } | |
67 | |
68 TxnState(TxnId anId, boolean isIdentity) { | |
69 theId = anId; | |
70 nonDestructive = isIdentity; | |
71 } | |
72 | |
73 public TxnId getId() { | |
74 return theId; | |
75 } | |
76 | |
77 int getStatus() throws TransactionException { | |
78 synchronized(this) { | |
79 return theState; | |
80 } | |
81 } | |
82 | |
83 public void add(TxnOp anOp) throws TransactionException { | |
84 synchronized (this) { | |
85 if (theState != TransactionConstants.ACTIVE) { | |
86 TxnManager.theLogger.log(Level.SEVERE, | |
87 "Txn not in active state at addOp"); | |
88 throw new TransactionException("Transaction no longer active"); | |
89 } else { | |
90 theOperations.add(anOp); | |
91 } | |
92 } | |
93 } | |
94 | |
95 /** | |
96 * A move to voting state indicates this transaction is going to be resolved | |
97 * and thus cannot have any further actions assigned to it. Note that | |
98 * voting state will only be asserted if the transaction is currently | |
99 * active. Voting is a transient state and only exists temporarily whilst | |
100 * we move to a resolved state. It's only benefit is that it renders | |
101 * a transaction's state unchangeable and allows us to make binding | |
102 * decisions such as whether we need to log to disk. | |
103 */ | |
104 int vote() throws TransactionException { | |
105 int myState; | |
106 | |
107 synchronized (this) { | |
108 | |
109 if (theState == TransactionConstants.ACTIVE) { | |
110 theState = TransactionConstants.VOTING; | |
111 } | |
112 | |
113 myState = theState; | |
114 } | |
115 | |
116 return myState; | |
117 } | |
118 | |
119 /** | |
120 * @todo Decide if ths DiskTxn is required here - probably not? | |
121 */ | |
122 int prepare(boolean needsRestore) | |
123 throws UnknownTransactionException, IOException { | |
124 | |
125 if (needsRestore) { | |
126 /* | |
127 If we're doing restore, we're single threaded and don't need | |
128 the protection of the lock (getLock()) | |
129 */ | |
130 // DiskTxn myTxn = DiskTxn.newTxn(); | |
131 | |
132 for (int i = (theOperations.size() - 1); i > -1; i--) { | |
133 TxnOp myOp = (TxnOp) theOperations.get(i); | |
134 myOp.restore(this); | |
135 } | |
136 | |
137 // myTxn.commit(); | |
138 } | |
139 | |
140 int myStatus; | |
141 | |
142 synchronized(this) { | |
143 if (theState == TransactionConstants.VOTING) { | |
144 theState = TransactionConstants.PREPARED; | |
145 } | |
146 | |
147 /* | |
148 The state may not have been ACTIVE, we might already have | |
149 been prepared or even got to commited/aborted and we need to | |
150 reflect that in the status we return. | |
151 */ | |
152 myStatus = theState; | |
153 | |
154 } | |
155 | |
156 return myStatus; | |
157 } | |
158 | |
159 void commit() | |
160 throws UnknownTransactionException, IOException { | |
161 | |
162 synchronized(this) { | |
163 if (theState != TransactionConstants.PREPARED) { | |
164 TxnManager.theLogger.log(Level.SEVERE, | |
165 "Txn not prepared before commit"); | |
166 throw new UnknownTransactionException(); | |
167 } | |
168 | |
169 /* | |
170 * We can afford to set the state beforehand because if this doesn't | |
171 * work we have serious problems and it's very unlikely a second attempt | |
172 * will succeed/do any good. | |
173 */ | |
174 theState = TransactionConstants.COMMITTED; | |
175 | |
176 /* | |
177 * Having changed the state we can be sure no one else will do the | |
178 * same thing again and it's better to not hold the lock across the | |
179 * below which might require I/O | |
180 */ | |
181 } | |
182 | |
183 /* | |
184 * MUST BE DONE IN REVERSE ORDER - WE WANT A TAKE TO BE APPLIED | |
185 * BEFORE WE HIT THE PREVIOUS WRITE. THIS ALLOWS US TO GENERATE | |
186 * APPROPRIATE EVENTS FOR WRITES READY FOR NOTIFY - i.e. we | |
187 * want the disk system to be aware that something has been | |
188 * deleted so that we can test that state and abandon generating | |
189 * a write event in those cases. This saves adding code/structure | |
190 * to search other operations to verify whether a write generates | |
191 * an event or not. | |
192 */ | |
193 | |
194 // StringBuffer myTxn = new StringBuffer(theId + " "); | |
195 | |
196 for (int i = (theOperations.size() - 1); i > -1; i--) { | |
197 TxnOp myOp = (TxnOp) theOperations.get(i); | |
198 myOp.commit(this); | |
199 | |
200 // myTxn = myTxn.append(myOp.toString() + " "); | |
201 } | |
202 | |
203 // TxnManager.theLogger.log(Level.INFO, myTxn.toString()); | |
204 } | |
205 | |
206 void abort() | |
207 throws UnknownTransactionException, IOException { | |
208 | |
209 synchronized(this) { | |
210 /* | |
211 Transaction pinger may attempt abort on a just finalized transaction | |
212 cos the transaction manager has forgotten about it. We need to | |
213 handle that by refusing to abort when we're finalized (committed | |
214 or aborted). | |
215 */ | |
216 if ((theState == TransactionConstants.COMMITTED) || | |
217 (theState == TransactionConstants.ABORTED)) { | |
218 TxnManager.theLogger.log(Level.SEVERE, | |
219 "Txn already finalized"); | |
220 throw new UnknownTransactionException(); | |
221 } | |
222 | |
223 /* | |
224 * We can afford to set the state beforehand because if this doesn't | |
225 * work we have serious problems and it's very unlikely a second attempt | |
226 * will succeed/do any good. | |
227 */ | |
228 theState = TransactionConstants.ABORTED; | |
229 | |
230 /* | |
231 * Having changed the state we can be sure no one else will do the | |
232 * same thing again and it's better to not hold the lock across the | |
233 * below which might require I/O | |
234 */ | |
235 } | |
236 | |
237 Iterator myOps = theOperations.iterator(); | |
238 | |
239 // No events generated so we can use whatever order we like | |
240 while (myOps.hasNext()) { | |
241 TxnOp myOp = (TxnOp) myOps.next(); | |
242 myOp.abort(this); | |
243 } | |
244 } | |
245 | |
246 public boolean isIdentity() { | |
247 return nonDestructive; | |
248 } | |
249 | |
250 public boolean isNull() { | |
251 return theId.isNull(); | |
252 } | |
253 | |
254 public boolean hasNoOps() { | |
255 return (theOperations.size() == 0); | |
256 } | |
257 | |
258 public String toString() { | |
259 StringBuffer myOps = new StringBuffer(); | |
260 | |
261 for (int i = 0; i < theOperations.size(); i++) { | |
262 // For the first operation, we adopt the time of the | |
263 // enclosing command so drop the hyphen | |
264 if (i == 0) | |
265 myOps.append(" " + theOperations.get(i).toString() + " : " + | |
266 theId + "\n"); | |
267 else | |
268 myOps.append("- : " + theOperations.get(i).toString() + " : " + | |
269 theId + "\n"); | |
270 } | |
271 | |
272 return myOps.toString(); | |
273 } | |
274 | |
275 void doFinalize() { | |
276 /* | |
277 * Signal any associated listeners - we only want to do this during live | |
278 * operations | |
279 */ | |
280 QueueEvent myEvent = | |
281 new QueueEvent(QueueEvent.TRANSACTION_ENDED, | |
282 this, null); | |
283 EventQueue.get().add(myEvent); | |
284 } | |
285 | |
286 public void test() throws TransactionException { | |
287 synchronized (this) { | |
288 if (theState != TransactionConstants.ACTIVE) { | |
289 TxnManager.theLogger.log(Level.SEVERE, | |
290 "Txn not in active state at addOp"); | |
291 throw new TransactionException("Transaction no longer active"); | |
292 } | |
293 } | |
294 } | |
295 } |