KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > exolab > jms > tranlog > TransactionLog


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 2001-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  *
44  * $Id: TransactionLog.java,v 1.1 2004/11/26 01:51:01 tanderson Exp $
45  *
46  * Date Author Changes
47  * 20/11/2001 jima Created
48  */

49 package org.exolab.jms.tranlog;
50
51
52 import java.io.BufferedInputStream JavaDoc;
53 import java.io.BufferedOutputStream JavaDoc;
54 import java.io.DataInputStream JavaDoc;
55 import java.io.DataOutputStream JavaDoc;
56 import java.io.File JavaDoc;
57 import java.io.FileInputStream JavaDoc;
58 import java.io.FileNotFoundException JavaDoc;
59 import java.io.FileOutputStream JavaDoc;
60 import java.io.IOException JavaDoc;
61 import java.util.HashMap JavaDoc;
62 import java.util.LinkedList JavaDoc;
63
64 import org.apache.commons.logging.Log;
65 import org.apache.commons.logging.LogFactory;
66
67
68 /**
69  * The resource manager uses transaction logs to record the persistent records
70  * for the resource manager in case of recovery. All records are logged
71  * sequentially and each records has an associated XID. Log files have a finite
72  * size, after which they are closed and a new log file is opened. There is
73  * only one current transaction log file per resource manager.
74  */

75 public class TransactionLog {
76
77     /**
78      * The name of this log file
79      */

80     private String JavaDoc _name = null;
81
82     /**
83      * Maintains the running total of the file size
84      */

85     private long _size = 0;
86
87     /**
88      * Cache the DataOutputStream handle
89      */

90     private transient DataOutputStream JavaDoc _dos = null;
91
92     /**
93      * The logger
94      */

95     private static final Log _log = LogFactory.getLog(TransactionLog.class);
96
97
98     /**
99      * Create a transaction log with the specified name, which includes the
100      * directory it will reside in. If the create flag is true then it will
101      * create the log file. If the create flag is false then it will assume
102      * that the log file already exists and will attempt to open it.
103      * <p>
104      * Attempting to create a file that already exists or open a non-exisitent
105      * log file with throw the TransactionLogException exception.
106      *
107      * @param name - the name of the transaction log absolute or final
108      * @param vreate - flag inidicating whether to open or create the log
109      * @throws TransactionLogException
110      */

111     public TransactionLog(String JavaDoc name, boolean create)
112         throws TransactionLogException {
113         if ((name == null) ||
114             (name.length() == 0)) {
115             throw new IllegalArgumentException JavaDoc("Can't specify a null or empty name");
116         }
117
118         _name = name;
119         File JavaDoc file = new File JavaDoc(name);
120
121         // check if the file needs to be created and whether it already
122
// exists.
123
if (create) {
124             if (file.exists()) {
125                 throw new TransactionLogException(name +
126                     " already exists");
127             } else {
128                 try {
129                     (new FileOutputStream JavaDoc(file)).close();
130                 } catch (Exception JavaDoc exception) {
131                     // rethrow the exception
132
throw new TransactionLogException(
133                         "Failed to create the log file " + name + " b/c" +
134                         exception);
135                 }
136             }
137         } else {
138             // check to see if a file needs to be open and that it actually
139
// exists.
140
if (!file.exists()) {
141                 throw new TransactionLogException(name + " does not exists");
142             }
143         }
144
145         // set the size of the file
146
_size = (new File JavaDoc(name)).length();
147     }
148
149     /**
150      * Return the name of the transaction log file
151      *
152      * @return String
153      */

154     public String JavaDoc getName() {
155         return _name;
156     }
157
158     /**
159      * Add an {@link StateTransactionLogEntry} using the specified txid,
160      * rid and state
161      *
162      * @param txid - the transaction identifier
163      * @param expiry - expiry time for the transaction
164      * @param rid - the resource identifier
165      * @param state - the transaction log state
166      * @throws TransactionLogException - if the entry cannot be created
167      */

168     public synchronized void logTransactionState(ExternalXid txid, long expiry,
169                                                  String JavaDoc rid,
170                                                  TransactionState state)
171         throws TransactionLogException {
172         try {
173             StateTransactionLogEntry entry = new StateTransactionLogEntry(txid, rid);
174             entry.setState(state);
175             entry.setExpiryTime(expiry);
176
177             DataOutputStream JavaDoc dos = getOutputStream();
178             byte[] blob = SerializationHelper.serialize(entry);
179             dos.writeLong(blob.length);
180             dos.write(blob, 0, blob.length);
181             dos.flush();
182
183             // update the size
184
_size += blob.length;
185         } catch (Exception JavaDoc exception) {
186             throw new TransactionLogException("Error in logTransactionState " +
187                 exception.toString());
188         }
189     }
190
191     /**
192      * Add an {@link DataTransactionLogEntry} using the specified txid,
193      * rid and data
194      *
195      * @param txid - the transaction identifier
196      * @param expiry - transaction expiry time
197      * @param rid - the resource identifier
198      * @param data - the opaque data to write
199      * @throws TransactionLogException - if the entry cannot be created
200      */

201     public synchronized void logTransactionData(ExternalXid txid, long expiry, String JavaDoc rid,
202                                                 Object JavaDoc data)
203         throws TransactionLogException {
204         try {
205             DataTransactionLogEntry entry = new DataTransactionLogEntry(txid, rid);
206             entry.setData(data);
207             entry.setExpiryTime(expiry);
208
209             DataOutputStream JavaDoc dos = getOutputStream();
210             byte[] blob = SerializationHelper.serialize(entry);
211             dos.writeLong(blob.length);
212             dos.write(blob, 0, blob.length);
213             dos.flush();
214
215             // update the size
216
_size += blob.length;
217         } catch (Exception JavaDoc exception) {
218             throw new TransactionLogException("Error in logTransactionData " +
219                 exception.toString());
220         }
221     }
222
223     /**
224      * Close the transaction log
225      *
226      * @throws TransactionLogException - if it fails to close the log
227      */

228     public void close()
229         throws TransactionLogException {
230         try {
231             if (_dos != null) {
232                 _dos.close();
233             }
234         } catch (IOException JavaDoc exception) {
235             throw new TransactionLogException("Error in close " +
236                 exception.toString());
237         }
238     }
239
240     /**
241      * Return the size of the transaction log file.
242      *
243      * @return long - the length of the file
244      */

245     public long size() {
246         return _size;
247     }
248
249     /**
250      * Force a recovery of this log file. This will close the output file stream
251      * if one is opened and then read each entry from the log file and send it to
252      * the specified listener, if one is allocated.
253      * <p>
254      * The returned data structure is a HashMap, where the key is a
255      * {@link ExternalXid} and the entries are LinkedList of {@link
256      * BaseTransactionLogEntry} objects
257      *
258      * @return HashMap - a list of open transactions
259      * @throws TransactionLogException - if there is a prob recovering
260      */

261     public synchronized HashMap JavaDoc recover()
262         throws TransactionLogException {
263         return getOpenTransactionList();
264     }
265
266     /**
267      * Check if we can garbage collect this transaction log. It will go through
268      * the log file and check to see whether there are any open transaction. If
269      * there are no open transactions the it is a candidate for garage collection
270      *
271      * @return boolean - true if we can garbage collect; false otherwise
272      */

273     public synchronized boolean canGarbageCollect() {
274         boolean result = false;
275
276         try {
277             HashMap JavaDoc records = getOpenTransactionList();
278             if (records.size() == 0) {
279                 result = true;
280             }
281         } catch (Exception JavaDoc ignore) {
282             ignore.printStackTrace();
283         }
284
285         return result;
286     }
287
288     /**
289      * Destroy this transaction log, which basically removes it from the
290      * file system
291      *
292      * @throws TransactionLogException
293      */

294     public synchronized void destroy()
295         throws TransactionLogException {
296         try {
297             close();
298             if (!(new File JavaDoc(_name)).delete()) {
299                 _log.error("Failed to destroy " + _name);
300             }
301         } catch (Exception JavaDoc exception) {
302             throw new TransactionLogException("Error in destroy " +
303                 exception.toString());
304
305         }
306     }
307
308     // override Object.equals
309
public boolean equals(Object JavaDoc obj) {
310         boolean result = false;
311
312         if ((obj instanceof TransactionLog) &&
313             (((TransactionLog) obj)._name.equals(_name))) {
314             result = true;
315         }
316
317         return result;
318     }
319
320     /**
321      * Return an instance of the output stream. If one does not exist then
322      * create it.
323      *
324      * @return DataOutputStream - the output stream
325      */

326     private DataOutputStream JavaDoc getOutputStream()
327         throws IOException JavaDoc, FileNotFoundException JavaDoc {
328         if (_dos == null) {
329             _dos = new DataOutputStream JavaDoc(
330                 new BufferedOutputStream JavaDoc(
331                     new FileOutputStream JavaDoc(_name, true)));
332         }
333
334         return _dos;
335     }
336
337     /**
338      * Return a list of open transactions in a HashMap. The key is the transaction
339      * id and the data is a vector of associated data records in a LinkedList
340      *
341      * @return HashMap
342      * @throws TransactionLogException - if there is a prob recovering
343      */

344     private HashMap JavaDoc getOpenTransactionList()
345         throws TransactionLogException {
346
347         HashMap JavaDoc records = new HashMap JavaDoc();
348
349         // if the output stream is opened then close it
350
try {
351             if (_dos != null) {
352                 _dos.close();
353                 _dos = null;
354             }
355         } catch (Exception JavaDoc exception) {
356             throw new TransactionLogException("Error in recover " +
357                 exception.toString());
358         }
359
360
361         FileInputStream JavaDoc fis = null;
362         try {
363             fis = new FileInputStream JavaDoc(_name);
364             DataInputStream JavaDoc dis = new DataInputStream JavaDoc(new BufferedInputStream JavaDoc(fis));
365
366             while (dis.available() > 0) {
367                 byte[] blob = new byte[(int) dis.readLong()];
368                 dis.readFully(blob);
369                 Object JavaDoc object = SerializationHelper.deserialize(blob);
370                 if (object instanceof StateTransactionLogEntry) {
371                     StateTransactionLogEntry state = (StateTransactionLogEntry) object;
372                     LinkedList JavaDoc list = null;
373                     switch (state.getState().getOrd()) {
374                         case TransactionState.OPENED_ORD:
375                             if (records.containsKey(state.getExternalXid())) {
376                                 _log.error("OPENED_ORD : Transaction log is inconsistent");
377                                 continue;
378                             }
379
380                             list = new LinkedList JavaDoc();
381                             records.put(state.getExternalXid(), list);
382                             list.add(state);
383                             break;
384
385                         case TransactionState.PREPARED_ORD:
386                             list = (LinkedList JavaDoc) records.get(state.getExternalXid());
387                             if (list == null) {
388                                 _log.error("PREPARED_ORD : Transaction log is inconsistent");
389                                 continue;
390                             }
391
392                             list.add(state);
393                             break;
394
395                         case TransactionState.CLOSED_ORD:
396                             if (records.get(state.getExternalXid()) == null) {
397                                 _log.error("CLOSED_ORD : Transaction log is inconsistent");
398                                 continue;
399                             }
400
401                             records.remove(state.getExternalXid());
402                             break;
403
404                         default:
405                             break;
406                     }
407                 } else if (object instanceof DataTransactionLogEntry) {
408                     DataTransactionLogEntry data = (DataTransactionLogEntry) object;
409                     LinkedList JavaDoc list = (LinkedList JavaDoc) records.get(data.getExternalXid());
410                     if (list == null) {
411                         _log.error("DATA : Transaction log is inconsistent");
412                         continue;
413                     }
414
415                     list.add(data);
416                 } else {
417                     System.err.println("There is no support for log entry " +
418                         "records of type " + object.getClass().getName());
419                 }
420
421             }
422         } catch (Exception JavaDoc exception) {
423             throw new TransactionLogException("Error in recover " +
424                 exception.toString());
425         } finally {
426             if (fis != null) {
427                 try {
428                     fis.close();
429                 } catch (Exception JavaDoc exception) {
430                     throw new TransactionLogException("Error in recover " +
431                         exception.toString());
432                 }
433             }
434         }
435
436         return records;
437
438     }
439
440 } //-- TransactionLog
441
Popular Tags