1 49 package org.exolab.jms.tranlog; 50 51 52 import java.io.BufferedInputStream ; 53 import java.io.BufferedOutputStream ; 54 import java.io.DataInputStream ; 55 import java.io.DataOutputStream ; 56 import java.io.File ; 57 import java.io.FileInputStream ; 58 import java.io.FileNotFoundException ; 59 import java.io.FileOutputStream ; 60 import java.io.IOException ; 61 import java.util.HashMap ; 62 import java.util.LinkedList ; 63 64 import org.apache.commons.logging.Log; 65 import org.apache.commons.logging.LogFactory; 66 67 68 75 public class TransactionLog { 76 77 80 private String _name = null; 81 82 85 private long _size = 0; 86 87 90 private transient DataOutputStream _dos = null; 91 92 95 private static final Log _log = LogFactory.getLog(TransactionLog.class); 96 97 98 111 public TransactionLog(String name, boolean create) 112 throws TransactionLogException { 113 if ((name == null) || 114 (name.length() == 0)) { 115 throw new IllegalArgumentException ("Can't specify a null or empty name"); 116 } 117 118 _name = name; 119 File file = new File (name); 120 121 if (create) { 124 if (file.exists()) { 125 throw new TransactionLogException(name + 126 " already exists"); 127 } else { 128 try { 129 (new FileOutputStream (file)).close(); 130 } catch (Exception exception) { 131 throw new TransactionLogException( 133 "Failed to create the log file " + name + " b/c" + 134 exception); 135 } 136 } 137 } else { 138 if (!file.exists()) { 141 throw new TransactionLogException(name + " does not exists"); 142 } 143 } 144 145 _size = (new File (name)).length(); 147 } 148 149 154 public String getName() { 155 return _name; 156 } 157 158 168 public synchronized void logTransactionState(ExternalXid txid, long expiry, 169 String 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 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 _size += blob.length; 185 } catch (Exception exception) { 186 throw new TransactionLogException("Error in logTransactionState " + 187 exception.toString()); 188 } 189 } 190 191 201 public synchronized void logTransactionData(ExternalXid txid, long expiry, String rid, 202 Object data) 203 throws TransactionLogException { 204 try { 205 DataTransactionLogEntry entry = new DataTransactionLogEntry(txid, rid); 206 entry.setData(data); 207 entry.setExpiryTime(expiry); 208 209 DataOutputStream 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 _size += blob.length; 217 } catch (Exception exception) { 218 throw new TransactionLogException("Error in logTransactionData " + 219 exception.toString()); 220 } 221 } 222 223 228 public void close() 229 throws TransactionLogException { 230 try { 231 if (_dos != null) { 232 _dos.close(); 233 } 234 } catch (IOException exception) { 235 throw new TransactionLogException("Error in close " + 236 exception.toString()); 237 } 238 } 239 240 245 public long size() { 246 return _size; 247 } 248 249 261 public synchronized HashMap recover() 262 throws TransactionLogException { 263 return getOpenTransactionList(); 264 } 265 266 273 public synchronized boolean canGarbageCollect() { 274 boolean result = false; 275 276 try { 277 HashMap records = getOpenTransactionList(); 278 if (records.size() == 0) { 279 result = true; 280 } 281 } catch (Exception ignore) { 282 ignore.printStackTrace(); 283 } 284 285 return result; 286 } 287 288 294 public synchronized void destroy() 295 throws TransactionLogException { 296 try { 297 close(); 298 if (!(new File (_name)).delete()) { 299 _log.error("Failed to destroy " + _name); 300 } 301 } catch (Exception exception) { 302 throw new TransactionLogException("Error in destroy " + 303 exception.toString()); 304 305 } 306 } 307 308 public boolean equals(Object 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 326 private DataOutputStream getOutputStream() 327 throws IOException , FileNotFoundException { 328 if (_dos == null) { 329 _dos = new DataOutputStream ( 330 new BufferedOutputStream ( 331 new FileOutputStream (_name, true))); 332 } 333 334 return _dos; 335 } 336 337 344 private HashMap getOpenTransactionList() 345 throws TransactionLogException { 346 347 HashMap records = new HashMap (); 348 349 try { 351 if (_dos != null) { 352 _dos.close(); 353 _dos = null; 354 } 355 } catch (Exception exception) { 356 throw new TransactionLogException("Error in recover " + 357 exception.toString()); 358 } 359 360 361 FileInputStream fis = null; 362 try { 363 fis = new FileInputStream (_name); 364 DataInputStream dis = new DataInputStream (new BufferedInputStream (fis)); 365 366 while (dis.available() > 0) { 367 byte[] blob = new byte[(int) dis.readLong()]; 368 dis.readFully(blob); 369 Object object = SerializationHelper.deserialize(blob); 370 if (object instanceof StateTransactionLogEntry) { 371 StateTransactionLogEntry state = (StateTransactionLogEntry) object; 372 LinkedList 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 (); 381 records.put(state.getExternalXid(), list); 382 list.add(state); 383 break; 384 385 case TransactionState.PREPARED_ORD: 386 list = (LinkedList ) 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 list = (LinkedList ) 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 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 exception) { 430 throw new TransactionLogException("Error in recover " + 431 exception.toString()); 432 } 433 } 434 } 435 436 return records; 437 438 } 439 440 } | Popular Tags |