KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > fr > dyade > aaa > util > JTransaction


1 /*
2  * Copyright (C) 2001 - 2004 ScalAgent Distributed Technologies
3  * Copyright (C) 1996 - 2000 BULL
4  * Copyright (C) 1996 - 2000 INRIA
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA.
20  */

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 JavaDoc EMPTY_STRING = new String JavaDoc();
28
29   private File dir = null;
30
31   static private final String JavaDoc LOG = "log";
32   private RandomAccessFile logFile = null;
33   private Hashtable log = null;
34
35   // SAVE and DELETE should be static attribute of Operation inner class.
36
// Unfortunatly it's unsupported in Java 1.1.x.
37
static final int SAVE = 1;
38   static final int DELETE = 2;
39
40   class Operation implements Serializable {
41     String JavaDoc dirName;
42     String JavaDoc name;
43     int type;
44     byte[] value = null;
45
46     Operation(int type, String JavaDoc dirName, String JavaDoc name) {
47       this(type, dirName, name, null);
48     }
49
50     Operation(int type, String JavaDoc dirName, String JavaDoc name, byte[] value) {
51       this.type = type;
52       this.dirName = dirName;
53       this.name = name;
54       this.value = value;
55     }
56   }
57
58   // State of the transaction monitor.
59
private int phase;
60
61   public JTransaction() {}
62
63   /**
64    * Tests if the Transaction component is persistent.
65    *
66    * @return true.
67    */

68   public boolean isPersistent() {
69     return true;
70   }
71
72   public void init(String JavaDoc 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     // Saves the transaction classname in order to prevent use of a
81
// different one after restart (see AgentServer.init).
82
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     // Read the log, then...
95
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       // Test the stop status then complete commit or rollback if needed
104
if (oldPhase == COMMIT) {
105     int op;
106     String JavaDoc name;
107     while (!(name = logFile.readUTF()).equals("")) {
108           String JavaDoc dirName = logFile.readUTF();
109           if (dirName.length() == 0) dirName = null;
110           Object JavaDoc 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   /**
131    * Returns the path of persistence directory.
132    *
133    * @return The path of persistence directory.
134    */

135   public String JavaDoc 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 JavaDoc exc) {
151       }
152     }
153     // Change the transaction state and save it.
154
setPhase(RUN);
155   }
156
157   public String JavaDoc[] getList(String JavaDoc prefix) {
158     return dir.list(new StartWithFilter(prefix));
159   }
160   
161   public void save(Serializable obj, String JavaDoc name) throws IOException {
162     save(obj, null, name);
163   }
164
165   public final void save(Serializable obj, String JavaDoc dirName, String JavaDoc 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 JavaDoc name) throws IOException {
174     saveByteArray(buf, null, name);
175   }
176
177   public void saveByteArray(byte[] buf,
178                             String JavaDoc dirName, String JavaDoc name) throws IOException {
179     if (phase == RUN) {
180       // We are during a transaction put the new state in the log.
181
Object JavaDoc key = OperationKey.newKey(dirName, name);
182       log.put(key, new Operation(SAVE, dirName, name, buf));
183     } else {
184       // Save the new state on the disk.
185
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 JavaDoc load(String JavaDoc name) throws IOException, ClassNotFoundException JavaDoc {
202     return load(null, name);
203   }
204
205   public final Object JavaDoc load(String JavaDoc dirName, String JavaDoc name) throws IOException, ClassNotFoundException JavaDoc {
206     Object JavaDoc obj;
207
208     if (phase == RUN) {
209       // first search in the log a new value for the object.
210
Object JavaDoc 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       // The object is no longer alive
220
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       // I'm not sure we can directly read the object without use
236
// a ByteArrayInputStream.
237
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 JavaDoc name) throws IOException {
249     return loadByteArray(null, name);
250   }
251
252   public byte[] loadByteArray(String JavaDoc dirName, String JavaDoc name) throws IOException {
253     if (phase == RUN) {
254       // first search in the log a new value for the object.
255
Object JavaDoc 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       // The object is no longer alive
262
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 JavaDoc name) {
291     delete(null, name);
292   }
293
294   public final void delete(String JavaDoc dirName, String JavaDoc name) {
295     if (phase == RUN) {
296       // We are during a transaction mark the object deleted in the log.
297
Object JavaDoc 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   /**
314    * Delete the specified directory if it is empty.
315    * Also recursively delete the parent directories if
316    * they are empty.
317    */

318   private void deleteDir(File dir) {
319     String JavaDoc[] children = dir.list();
320     // children may be null if dir doesn't exist any more.
321
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     // Save the log to disk
336
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     // Change the transaction state and save it.
405
setPhase(FREE);
406     notify();
407   }
408
409   /**
410    * Stops the transaction module.
411    * It waits all transactions termination, then the module is kept
412    * in a FREE 'ready to use' state.
413    */

414   public synchronized void stop() {
415     while (phase != FREE) {
416       try {
417     wait();
418       } catch (InterruptedException JavaDoc exc) {
419       }
420     }
421   }
422
423   /**
424    * Close the transaction module.
425    * It waits all transactions termination, the module will be initialized
426    * anew before reusing it.
427    */

428   public final synchronized void close() {
429     while (phase != FREE) {
430       // Wait for the transaction subsystem to be free
431
try {
432         wait();
433       } catch (InterruptedException JavaDoc 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 JavaDoc newKey(String JavaDoc dirName, String JavaDoc name) {
448       if (dirName == null) {
449         return name;
450       } else {
451         return new OperationKey(dirName, name);
452       }
453     }
454
455     private String JavaDoc dirName;
456     private String JavaDoc name;
457
458     private OperationKey(String JavaDoc dirName,
459                         String JavaDoc name) {
460       this.dirName = dirName;
461       this.name = name;
462     }
463
464     public int hashCode() {
465       // Should compute a specific one.
466
return dirName.hashCode();
467     }
468
469     public boolean equals(Object JavaDoc 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