1 21 package fr.dyade.aaa.util; 22 23 import java.io.*; 24 import java.util.*; 25 26 public final class JTransaction implements Transaction, JTransactionMBean { 27 public static final String EMPTY_STRING = new String (); 28 29 private File dir = null; 30 31 static private final String LOG = "log"; 32 private RandomAccessFile logFile = null; 33 private Hashtable log = null; 34 35 static final int SAVE = 1; 38 static final int DELETE = 2; 39 40 class Operation implements Serializable { 41 String dirName; 42 String name; 43 int type; 44 byte[] value = null; 45 46 Operation(int type, String dirName, String name) { 47 this(type, dirName, name, null); 48 } 49 50 Operation(int type, String dirName, String name, byte[] value) { 51 this.type = type; 52 this.dirName = dirName; 53 this.name = name; 54 this.value = value; 55 } 56 } 57 58 private int phase; 60 61 public JTransaction() {} 62 63 68 public boolean isPersistent() { 69 return true; 70 } 71 72 public void init(String path) throws IOException { 73 phase = INIT; 74 75 dir = new File(path); 76 if (!dir.exists()) dir.mkdir(); 77 if (!dir.isDirectory()) 78 throw new FileNotFoundException(path + " is not a directory."); 79 80 DataOutputStream dos = null; 83 try { 84 File tfc = new File(dir, "TFC"); 85 if (! tfc.exists()) { 86 dos = new DataOutputStream(new FileOutputStream(tfc)); 87 dos.writeUTF(getClass().getName()); 88 dos.flush(); 89 } 90 } finally { 91 if (dos != null) dos.close(); 92 } 93 94 int oldPhase = FREE; 96 97 logFile = new RandomAccessFile(new File(dir, LOG), "rw"); 98 log = new Hashtable(); 99 if (logFile.length() != 0) { 100 logFile.seek(0L); 101 oldPhase = logFile.readInt(); 102 103 if (oldPhase == COMMIT) { 105 int op; 106 String name; 107 while (!(name = logFile.readUTF()).equals("")) { 108 String dirName = logFile.readUTF(); 109 if (dirName.length() == 0) dirName = null; 110 Object key = OperationKey.newKey(dirName, name); 111 op = logFile.read(); 112 if (op == SAVE) { 113 byte buf[] = new byte[logFile.readInt()]; 114 logFile.readFully(buf); 115 log.put(key, new Operation(SAVE, dirName, name, buf)); 116 } else { 117 log.put(key, new Operation(op, dirName, name)); 118 } 119 } 120 } 121 _commit(); 122 } 123 setPhase(FREE); 124 } 125 126 public File getDir() { 127 return dir; 128 } 129 130 135 public String getPersistenceDir() { 136 return dir.getPath(); 137 } 138 139 private void setPhase(int newPhase) throws IOException { 140 logFile.seek(0L); 141 logFile.writeInt(newPhase); 142 logFile.getFD().sync(); 143 phase = newPhase; 144 } 145 146 public synchronized void begin() throws IOException { 147 while (phase != FREE) { 148 try { 149 wait(); 150 } catch (InterruptedException exc) { 151 } 152 } 153 setPhase(RUN); 155 } 156 157 public String [] getList(String prefix) { 158 return dir.list(new StartWithFilter(prefix)); 159 } 160 161 public void save(Serializable obj, String name) throws IOException { 162 save(obj, null, name); 163 } 164 165 public final void save(Serializable obj, String dirName, String name) throws IOException { 166 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 167 ObjectOutputStream oos = new ObjectOutputStream(bos); 168 oos.writeObject(obj); 169 oos.flush(); 170 saveByteArray(bos.toByteArray(), dirName, name); 171 } 172 173 public void saveByteArray(byte[] buf, String name) throws IOException { 174 saveByteArray(buf, null, name); 175 } 176 177 public void saveByteArray(byte[] buf, 178 String dirName, String name) throws IOException { 179 if (phase == RUN) { 180 Object key = OperationKey.newKey(dirName, name); 182 log.put(key, new Operation(SAVE, dirName, name, buf)); 183 } else { 184 File file; 186 if (dirName == null) { 187 file = new File(dir, name); 188 } else { 189 File parentDir = new File(dir, dirName); 190 if (!parentDir.exists()) { 191 parentDir.mkdirs(); 192 } 193 file = new File(parentDir, name); 194 } 195 FileOutputStream fos = new FileOutputStream(file); 196 fos.write(buf); 197 fos.close(); 198 } 199 } 200 201 public Object load(String name) throws IOException, ClassNotFoundException { 202 return load(null, name); 203 } 204 205 public final Object load(String dirName, String name) throws IOException, ClassNotFoundException { 206 Object obj; 207 208 if (phase == RUN) { 209 Object key = OperationKey.newKey(dirName, name); 211 Operation op = (Operation) log.get(key); 212 if (op != null) { 213 if (op.type == SAVE) { 214 ByteArrayInputStream bis = new ByteArrayInputStream(op.value); 215 ObjectInputStream ois = new ObjectInputStream(bis); 216 217 return ois.readObject(); 218 } else if (op.type == DELETE) { 219 return null; 221 } 222 } 223 } 224 225 try { 226 File file; 227 if (dirName == null) { 228 file = new File(dir, name); 229 } else { 230 File parentDir = new File(dir, dirName); 231 file = new File(parentDir, name); 232 } 233 FileInputStream fis = new FileInputStream(file); 234 235 ObjectInputStream ois = new ObjectInputStream(fis); 238 obj = ois.readObject(); 239 240 fis.close(); 241 } catch (FileNotFoundException exc) { 242 return null; 243 } 244 245 return obj; 246 } 247 248 public byte[] loadByteArray(String name) throws IOException { 249 return loadByteArray(null, name); 250 } 251 252 public byte[] loadByteArray(String dirName, String name) throws IOException { 253 if (phase == RUN) { 254 Object key = OperationKey.newKey(dirName, name); 256 Operation op = (Operation) log.get(key); 257 if (op != null) { 258 if (op.type == SAVE) { 259 return op.value; 260 } else if (op.type == DELETE) { 261 return null; 263 } 264 } 265 } 266 267 try { 268 File file; 269 if (dirName == null) { 270 file = new File(dir, name); 271 } else { 272 File parentDir = new File(dir, dirName); 273 file = new File(parentDir, name); 274 } 275 FileInputStream fis = new FileInputStream(file); 276 byte[] buf = new byte[(int) file.length()]; 277 for (int nb=0; nb<buf.length; ) { 278 int ret = fis.read(buf, nb, buf.length-nb); 279 if (ret == -1) throw new EOFException(); 280 nb += ret; 281 } 282 fis.close(); 283 284 return buf; 285 } catch (FileNotFoundException exc) { 286 return null; 287 } 288 } 289 290 public void delete(String name) { 291 delete(null, name); 292 } 293 294 public final void delete(String dirName, String name) { 295 if (phase == RUN) { 296 Object key = OperationKey.newKey(dirName, name); 298 log.put(key, new Operation(DELETE, dirName, name)); 299 } else { 300 File file; 301 if (dirName == null) { 302 file = new File(dir, name); 303 file.delete(); 304 } else { 305 File parentDir = new File(dir, dirName); 306 file = new File(parentDir, name); 307 file.delete(); 308 deleteDir(parentDir); 309 } 310 } 311 } 312 313 318 private void deleteDir(File dir) { 319 String [] children = dir.list(); 320 if (children != null && 322 children.length == 0) { 323 dir.delete(); 324 if (dir.getAbsolutePath().length() > 325 this.dir.getAbsolutePath().length()) { 326 deleteDir(dir.getParentFile()); 327 } 328 } 329 } 330 331 public synchronized void commit() throws IOException { 332 if (phase != RUN) 333 throw new NotActiveException("Can not commit inexistent transaction."); 334 335 logFile.seek(4L); 337 for (Enumeration e = log.elements(); e.hasMoreElements(); ) { 338 Operation op = (Operation) e.nextElement(); 339 logFile.writeUTF(op.name); 340 if (op.dirName != null) { 341 logFile.writeUTF(op.dirName); 342 } else { 343 logFile.writeUTF(EMPTY_STRING); 344 } 345 logFile.writeByte(op.type); 346 if (op.type == SAVE) { 347 logFile.writeInt(op.value.length); 348 logFile.write(op.value); 349 } 350 } 351 logFile.writeUTF(""); 352 setPhase(COMMIT); 353 _commit(); 354 log.clear(); 355 } 356 357 private void _commit() throws IOException { 358 for (Enumeration e = log.elements(); e.hasMoreElements(); ) { 359 Operation op = (Operation) e.nextElement(); 360 361 if (op.type == SAVE) { 362 File file; 363 if (op.dirName == null) { 364 file = new File(dir, op.name); 365 } else { 366 File parentDir = new File(dir, op.dirName); 367 if (!parentDir.exists()) { 368 parentDir.mkdirs(); 369 } 370 file = new File(parentDir, op.name); 371 } 372 FileOutputStream fos = new FileOutputStream(file); 373 fos.write(op.value); 374 fos.getFD().sync(); 375 fos.close(); 376 } else if (op.type == DELETE) { 377 File file; 378 if (op.dirName == null) { 379 file = new File(dir, op.name); 380 file.delete(); 381 } else { 382 File parentDir = new File(dir, op.dirName); 383 file = new File(parentDir, op.name); 384 file.delete(); 385 deleteDir(parentDir); 386 } 387 } else { 388 throw new InvalidObjectException("Unknow object in log."); 389 } 390 } 391 } 392 393 public synchronized void rollback() throws IOException { 394 if (phase != RUN) 395 throw new NotActiveException("Can not rollback inexistent transaction."); 396 setPhase(ROLLBACK); 397 log.clear(); 398 } 399 400 public synchronized void release() throws IOException { 401 if ((phase != COMMIT) && (phase != ROLLBACK)) 402 throw new NotActiveException("Can not release transaction."); 403 404 setPhase(FREE); 406 notify(); 407 } 408 409 414 public synchronized void stop() { 415 while (phase != FREE) { 416 try { 417 wait(); 418 } catch (InterruptedException exc) { 419 } 420 } 421 } 422 423 428 public final synchronized void close() { 429 while (phase != FREE) { 430 try { 432 wait(); 433 } catch (InterruptedException exc) { 434 } 435 } 436 437 try { 438 setPhase(FINALIZE); 439 logFile.close(); 440 setPhase(INIT); 441 } catch (IOException exc) { 442 } 443 } 444 445 private static class OperationKey { 446 447 static Object newKey(String dirName, String name) { 448 if (dirName == null) { 449 return name; 450 } else { 451 return new OperationKey(dirName, name); 452 } 453 } 454 455 private String dirName; 456 private String name; 457 458 private OperationKey(String dirName, 459 String name) { 460 this.dirName = dirName; 461 this.name = name; 462 } 463 464 public int hashCode() { 465 return dirName.hashCode(); 467 } 468 469 public boolean equals(Object obj) { 470 if (this == obj) return true; 471 if (obj instanceof OperationKey) { 472 OperationKey opk = (OperationKey)obj; 473 if (opk.name.length() != name.length()) return false; 474 if (opk.dirName.length() != dirName.length()) return false; 475 if (!opk.dirName.equals(dirName)) return false; 476 return opk.name.equals(name); 477 } 478 return false; 479 } 480 } 481 } 482 | Popular Tags |