fr/dyade/aaa/util/ATransaction.java

00001 /*
00002  * Copyright (C) 2001 - 2008 ScalAgent Distributed Technologies
00003  * Copyright (C) 1996 - 2000 BULL
00004  * Copyright (C) 1996 - 2000 INRIA
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or any later version.
00010  * 
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  * 
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
00019  * USA.
00020  *
00021  * Initial developer(s): ScalAgent Distributed Technologies
00022  * Contributor(s): Alexander Fedorowicz
00023  */
00024 package fr.dyade.aaa.util;
00025 
00026 import java.io.*;
00027 import java.util.*;
00028 
00029 import org.objectweb.util.monolog.api.BasicLevel;
00030 import org.objectweb.util.monolog.api.Logger;
00031 
00032 import fr.dyade.aaa.agent.Debug;
00033 
00034 public class ATransaction implements Transaction, Runnable {
00035   // State of the transaction monitor.
00036   private int phase;
00037 
00038   private static Logger logmon = null;
00039 
00040   final static int CLEANUP_THRESHOLD_COMMIT = 9600;
00041   final static int CLEANUP_THRESHOLD_OPERATION = 36000;
00042   final static int CLEANUP_THRESHOLD_SIZE = 8 * Mb;
00043 
00044   private int commitCount = 0;    // Number of commited transaction in clog.
00045   private int operationCount = 0; // Number of operations reported to clog.
00046   private int cumulativeSize = 0; // Byte amount in clog.
00047 
00048   private class Context {
00049     Hashtable log = null;
00050     ByteArrayOutputStream bos = null;
00051     ObjectOutputStream oos = null;
00052 
00053     Context() {
00054       log = new Hashtable(15);
00055       bos = new ByteArrayOutputStream(256);
00056     }
00057   }
00058 
00059   static private final byte[] OOS_STREAM_HEADER = {
00060                                                    (byte)((ObjectStreamConstants.STREAM_MAGIC >>> 8) & 0xFF),
00061                                                    (byte)((ObjectStreamConstants.STREAM_MAGIC >>> 0) & 0xFF),
00062                                                    (byte)((ObjectStreamConstants.STREAM_VERSION >>> 8) & 0xFF),
00063                                                    (byte)((ObjectStreamConstants.STREAM_VERSION >>> 0) & 0xFF)
00064   };
00065 
00072   private ThreadLocal perThreadContext = null;
00078   private Hashtable clog = null;
00083   private Hashtable plog = null;
00084 
00085   private File dir = null;
00086 
00087   static private final String LOCK = "lock";
00088   static private final String LOG = "log";
00089   static private final String PLOG = "plog";
00090   private File lockFile = null;
00091   protected File logFilePN = null;
00092   protected File plogFilePN = null;
00093 
00094   // State of the garbage.
00095   private boolean garbage;
00096   private Object lock = null;
00097   private boolean isRunning;
00098 
00099   private Thread gThread = null;
00100 
00101   static final boolean debug = false;
00102 
00103   public ATransaction() {}
00104 
00105   public boolean isPersistent() {
00106     return true;
00107   }
00108 
00109   public final void init(String path) throws IOException {
00110     phase = INIT;
00111 
00112     logmon = Debug.getLogger(Debug.A3Debug + ".Transaction");
00113     if (logmon.isLoggable(BasicLevel.INFO))
00114       logmon.log(BasicLevel.INFO, "ATransaction, init()");
00115 
00116     //  Search for log files: plog then clog, reads it, then apply all
00117     // committed operation, finally deletes it.
00118     dir = new File(path);
00119     if (!dir.exists()) dir.mkdir();
00120     if (!dir.isDirectory())
00121       throw new FileNotFoundException(path + " is not a directory.");
00122 
00123     // Saves the transaction classname in order to prevent use of a
00124     // different one after restart (see AgentServer.init).
00125     DataOutputStream ldos = null;
00126     try {
00127       lockFile = new File(dir, LOCK);
00128       if (! lockFile.createNewFile()) {
00129         logmon.log(BasicLevel.FATAL,
00130                    "ATransaction.init(): " +
00131                    "Either the server is already running, " + 
00132                    "either you have to remove lock file: " +
00133                    lockFile.getAbsolutePath());
00134         throw new IOException("Transaction already running.");
00135       }
00136       lockFile.deleteOnExit();
00137       File tfc = new File(dir, "TFC");
00138       if (! tfc.exists()) {
00139         ldos = new DataOutputStream(new FileOutputStream(tfc));
00140         ldos.writeUTF(getClass().getName());
00141         ldos.flush();
00142       }
00143     } finally {
00144       if (ldos != null) ldos.close();
00145     }
00146 
00147     logFilePN = new File(dir, LOG);
00148     plogFilePN = new File(dir, PLOG);
00149 
00150     Hashtable tempLog = new Hashtable();
00151     restart(tempLog, logFilePN);
00152     restart(tempLog, plogFilePN);
00153     commit(tempLog);
00154 
00155     plogFilePN.delete();
00156     logFilePN.delete();
00157 
00158     perThreadContext = new ThreadLocal() {
00159       protected synchronized Object initialValue() {
00160         return new Context();
00161       }
00162     };
00163 
00164     clog = new Hashtable(CLEANUP_THRESHOLD_OPERATION / 2);
00165     plog = new Hashtable(CLEANUP_THRESHOLD_OPERATION / 2);
00166 
00167     baos = new ByteArrayOutputStream(10 * Kb);
00168     dos = new DataOutputStream(baos);
00169 
00170     newLogFile();
00171 
00172     lock = new Object();
00173     garbage = false;
00174     gThread = new Thread(this, "TGarbage");
00175     gThread.start();
00176 
00177     if (logmon.isLoggable(BasicLevel.INFO))
00178       logmon.log(BasicLevel.INFO, "ATransaction, initialized");
00179 
00180     /* The Transaction subsystem is ready */
00181     setPhase(FREE);
00182   }
00183 
00184   private final void restart(Hashtable log, File logFilePN) throws IOException {
00185     if (logmon.isLoggable(BasicLevel.INFO))
00186       logmon.log(BasicLevel.INFO, "ATransaction, restart");
00187 
00188     if ((logFilePN.exists()) && (logFilePN.length() > 0)) {
00189       RandomAccessFile logFile = new RandomAccessFile(logFilePN, "r");
00190       try {
00191         Hashtable templog = new Hashtable();
00192         while (true) {
00193           int optype;
00194           String dirName;
00195           String name;
00196           while ((optype = logFile.read()) != ATOperation.COMMIT) {
00197             //  Gets all operations of one committed transaction then
00198             // adds them to specified log.
00199             dirName = logFile.readUTF();
00200             if (dirName.length() == 0) dirName = null;
00201             name = logFile.readUTF();
00202 
00203             Object key = ATOperationKey.newKey(dirName, name);
00204 
00205             ATOperation op = null;
00206             if (optype == ATOperation.SAVE) {
00207               byte buf[] = new byte[logFile.readInt()];
00208               logFile.readFully(buf);
00209               op = ATOperation.alloc(optype, dirName, name, buf);
00210               op = (ATOperation) templog.put(key, op);
00211             } else {
00212               op = ATOperation.alloc(optype, dirName, name);
00213               op = (ATOperation) templog.put(key, op);
00214             }
00215             if (op != null) op.free();
00216           }
00217           //  During this aggregation somes operation object can be lost due
00218           // to Hashtable collision...
00219           log.putAll(templog);
00220           templog.clear();
00221         }
00222       } catch (EOFException exc) {
00223         logFile.close();
00224       } catch (IOException exc) {
00225         logFile.close();
00226         throw exc;
00227       }
00228     }
00229     if (logmon.isLoggable(BasicLevel.INFO))
00230       logmon.log(BasicLevel.INFO, "ATransaction, started");
00231   }
00232 
00233   public final File getDir() {
00234     return dir;
00235   }
00236 
00237   private final void setPhase(int newPhase) {
00238     phase = newPhase;
00239   }
00240 
00241   public int getPhase() {
00242     return phase;
00243   }
00244 
00245   public String getPhaseInfo() {
00246     return PhaseInfo[phase];
00247   }
00248 
00249   public final synchronized void begin() throws IOException {
00250     while (phase != FREE) {
00251       try {
00252         wait();
00253       } catch (InterruptedException exc) {
00254       }
00255     }
00256     // Change the transaction state.
00257     setPhase(RUN);
00258   }
00259 
00260   // Be careful: only used in Server 
00261   public final String[] getList(String prefix) {
00262     return dir.list(new StartWithFilter(prefix));
00263   }
00264 
00265   public final void create(Serializable obj, String name) throws IOException {
00266     save(obj, null, name);
00267   }
00268 
00269   public final void save(Serializable obj, String name) throws IOException {
00270     save(obj, null, name);
00271   }
00272 
00273   public final void create(Serializable obj, String dirName, String name) throws IOException {
00274     save(obj, dirName, name);
00275   }
00276 
00277   public final void save(Serializable obj,
00278                          String dirName, String name) throws IOException {
00279     if (logmon.isLoggable(BasicLevel.DEBUG))
00280       logmon.log(BasicLevel.DEBUG, "ATransaction, save(" + dirName + ", " + name + ")");
00281 
00282     Context ctx = (Context) perThreadContext.get();
00283     if (ctx.oos == null) {
00284       ctx.bos.reset();
00285       ctx.oos = new ObjectOutputStream(ctx.bos);
00286     } else {
00287       ctx.oos.reset();
00288       ctx.bos.reset();
00289       ctx.bos.write(OOS_STREAM_HEADER, 0, 4);
00290     }
00291     ctx.oos.writeObject(obj);
00292     ctx.oos.flush();
00293 
00294     saveInLog(ctx.bos.toByteArray(), dirName, name, ctx.log, false);
00295   }
00296 
00301   public final void saveByteArray(byte[] buf, String name) throws IOException {
00302     saveByteArray(buf, null, name);
00303   }
00304 
00309   public final void saveByteArray(byte[] buf,
00310                                   String dirName, String name) throws IOException {
00311     saveInLog(buf,
00312               dirName, name,
00313               ((Context) perThreadContext.get()).log, true);
00314   }
00315 
00316   private final void saveInLog(byte[] buf,
00317                                String dirName, String name,
00318                                Hashtable log,
00319                                boolean copy) throws IOException {
00320     Object key = ATOperationKey.newKey(dirName, name);
00321     ATOperation op = ATOperation.alloc(ATOperation.SAVE, dirName, name, buf);
00322     ATOperation old = (ATOperation) log.put(key, op);
00323     if (copy) {
00324       if ((old != null) &&
00325           (old.type == ATOperation.SAVE) &&
00326           (old.value.length == buf.length)) {
00327         // reuse old buffer
00328         op.value = old.value;
00329       } else {
00330         // alloc a new one
00331         op.value = new byte[buf.length];
00332       }
00333       System.arraycopy(buf, 0, op.value, 0, buf.length);
00334     }
00335     if (old != null) old.free();
00336 
00337   }
00338 
00339   private final byte[] getFromLog(Hashtable log, Object key) throws IOException {
00340     // Searchs in the log a new value for the object.
00341     ATOperation op = (ATOperation) log.get(key);
00342     if (op != null) {
00343       if (op.type == ATOperation.SAVE) {
00344         return op.value;
00345       } else if (op.type == ATOperation.DELETE) {
00346         // The object was deleted.
00347         throw new FileNotFoundException();
00348       }
00349     }
00350     return null;
00351   }
00352 
00353   private final byte[] getFromLog(String dirName, String name) throws IOException {
00354     // First searchs in the logs a new value for the object.
00355     Object key = ATOperationKey.newKey(dirName, name);
00356     byte[] buf = getFromLog(((Context) perThreadContext.get()).log, key);
00357     if (buf != null) return buf;
00358 
00359     if (((buf = getFromLog(clog, key)) != null) ||
00360         ((buf = getFromLog(plog, key)) != null)) {
00361       return buf;
00362     }
00363     return null;  
00364   }
00365 
00366   public final Object load(String name) throws IOException, ClassNotFoundException {
00367     return load(null, name);
00368   }
00369 
00370   public final Object load(String dirName, String name) throws IOException, ClassNotFoundException {
00371     if (logmon.isLoggable(BasicLevel.DEBUG))
00372       logmon.log(BasicLevel.DEBUG, "ATransaction, load(" + dirName + ", " + name + ")");
00373 
00374     // First searchs in the logs a new value for the object.
00375     try {
00376       byte[] buf = getFromLog(dirName, name);
00377       if (buf != null) {
00378         ByteArrayInputStream bis = new ByteArrayInputStream(buf);
00379         ObjectInputStream ois = new ObjectInputStream(bis);       
00380         return ois.readObject();
00381       }
00382 
00383       // Gets it from disk.      
00384       File file;
00385       Object obj;
00386       if (dirName == null) {
00387         file = new File(dir, name);
00388       } else {
00389         File parentDir = new File(dir, dirName);
00390         file = new File(parentDir, name);
00391       }
00392       FileInputStream fis = new FileInputStream(file);
00393       ObjectInputStream ois = new ObjectInputStream(fis);
00394       obj = ois.readObject();
00395 
00396       fis.close();
00397       return obj;
00398     } catch (FileNotFoundException exc) {
00399       return null;
00400     }
00401   }
00402 
00403   public final byte[] loadByteArray(String name) throws IOException {
00404     return loadByteArray(null, name);
00405   }
00406 
00407   public final byte[] loadByteArray(String dirName, String name) throws IOException {
00408     // First searchs in the logs a new value for the object.
00409     try {
00410       byte[] buf = getFromLog(dirName, name);
00411       if (buf != null) return buf;
00412 
00413       // Gets it from disk.      
00414       File file;
00415       if (dirName == null) {
00416         file = new File(dir, name);
00417       } else {
00418         File parentDir = new File(dir, dirName);
00419         file = new File(parentDir, name);
00420       }
00421       FileInputStream fis = new FileInputStream(file);
00422       buf = new byte[(int) file.length()];
00423       for (int nb=0; nb<buf.length; ) {
00424         int ret = fis.read(buf, nb, buf.length-nb);
00425         if (ret == -1) throw new EOFException();
00426         nb += ret;
00427       }
00428       fis.close();
00429 
00430       return buf;
00431     } catch (FileNotFoundException exc) {
00432       return null;
00433     }
00434   }
00435 
00436   public final void delete(String name) {
00437     delete(null, name);
00438   }
00439 
00440   public final void delete(String dirName, String name) {
00441     Object key = ATOperationKey.newKey(dirName, name);
00442 
00443     Hashtable log = ((Context) perThreadContext.get()).log;
00444     ATOperation op = ATOperation.alloc(ATOperation.DELETE, dirName, name);
00445     op = (ATOperation) log.put(key, op);
00446     if (op != null) op.free();
00447   }
00448 
00449   static private final byte[] emptyUTFString = {0, 0};
00450 
00451   static private ByteArrayOutputStream baos = null;
00452   static private DataOutputStream dos = null;
00453 
00454   public void commit(boolean release) throws IOException {
00455     if (phase != RUN)
00456       throw new IllegalStateException("Can not commit.");
00457 
00458     if (logmon.isLoggable(BasicLevel.DEBUG))
00459       logmon.log(BasicLevel.DEBUG, "ATransaction, commit");
00460 
00461     commitCount += 1; // AF: Monitoring
00462     Hashtable log = ((Context) perThreadContext.get()).log;
00463     if (! log.isEmpty()) {
00464       ATOperation op = null;
00465       for (Enumeration e = log.elements(); e.hasMoreElements(); ) {
00466         op = (ATOperation) e.nextElement();
00467 
00468         operationCount += 1; // AF: Monitoring
00469         // Save the log to disk
00470         dos.write(op.type);
00471         if (op.dirName != null) {
00472           dos.writeUTF(op.dirName);
00473         } else {
00474           dos.write(emptyUTFString);
00475         }
00476         dos.writeUTF(op.name);
00477         if (op.type == ATOperation.SAVE) {
00478           dos.writeInt(op.value.length);
00479           dos.write(op.value);
00480           cumulativeSize += op.value.length; // AF: Monitoring
00481         }
00482 
00483         // Reports all committed operation in clog
00484         op = (ATOperation) clog.put(ATOperationKey.newKey(op.dirName, op.name), op);
00485         if (op != null) op.free();
00486       }
00487       dos.writeByte(ATOperation.COMMIT);
00488       dos.flush();
00489       logFile.write(baos.toByteArray());
00490       //     baos.writeTo(logFile);
00491       baos.reset();
00492 
00493       syncLogFile();
00494 
00495       log.clear();
00496     }
00497 
00498     if (logmon.isLoggable(BasicLevel.DEBUG))
00499       logmon.log(BasicLevel.DEBUG, "ATransaction, committed");
00500 
00501     setPhase(COMMIT);
00502 
00503     if (release) {
00504       release();
00505     }
00506   }
00507 
00508   protected RandomAccessFile logFile = null; 
00509   protected FileDescriptor logFD = null;
00510 
00511   protected void newLogFile() throws IOException {
00512     logFile = new RandomAccessFile(logFilePN, "rw");
00513     logFD = logFile.getFD();
00514   }
00515 
00516   protected void syncLogFile() throws IOException {
00517     logFD.sync();
00518   }
00519 
00520   public final synchronized void rollback() throws IOException {
00521     if (phase != RUN)
00522       throw new IllegalStateException("Can not rollback.");
00523 
00524     if (logmon.isLoggable(BasicLevel.DEBUG))
00525       logmon.log(BasicLevel.DEBUG, "ATransaction, rollback");
00526 
00527     setPhase(ROLLBACK);
00528     ((Context) perThreadContext.get()).log.clear();
00529   }
00530 
00531   public final synchronized void release() throws IOException {
00532     if ((phase != RUN) && (phase != COMMIT) && (phase != ROLLBACK))
00533       throw new IllegalStateException("Can not release transaction.");
00534 
00535     if (((commitCount > CLEANUP_THRESHOLD_COMMIT) ||
00536         (operationCount > CLEANUP_THRESHOLD_OPERATION) ||
00537         (cumulativeSize > CLEANUP_THRESHOLD_SIZE)) && !garbage) {
00538       synchronized (lock) {
00539         // wake-up the garbage thread
00540         garbage = true;
00541         // Change the transaction state.
00542         setPhase(GARBAGE);
00543         lock.notify();
00544       }
00545     } else {
00546       _release();
00547     }
00548   }
00549 
00550   private final synchronized void _release() {
00551     // Change the transaction state.
00552     setPhase(FREE);
00553     // wake-up an eventually user's thread in begin
00554     notify();
00555   }
00556 
00560   private final void commit(Hashtable log) throws IOException {
00561     if (logmon.isLoggable(BasicLevel.DEBUG))
00562       logmon.log(BasicLevel.DEBUG,
00563                  "ATransaction, Commit(" + log + ")");
00564 
00565     ATOperation op = null;
00566     for (Enumeration e = log.elements(); e.hasMoreElements(); ) {
00567       op = (ATOperation) e.nextElement();
00568 
00569       if (op.type == ATOperation.SAVE) {
00570         if (logmon.isLoggable(BasicLevel.DEBUG))
00571           logmon.log(BasicLevel.DEBUG,
00572                      "ATransaction, Save (" + op.dirName + ',' + op.name + ')');
00573 
00574         File file;
00575         if (op.dirName == null) {
00576           file = new File(dir, op.name);
00577         } else {
00578           File parentDir = new File(dir, op.dirName);
00579           if (!parentDir.exists()) {
00580             parentDir.mkdirs();
00581           }
00582           file = new File(parentDir, op.name);
00583         }
00584 
00585         FileOutputStream fos = new FileOutputStream(file);
00586         fos.write(op.value);
00587         fos.getFD().sync();
00588         fos.close();
00589       } else if (op.type == ATOperation.DELETE) {
00590         if (logmon.isLoggable(BasicLevel.DEBUG))
00591           logmon.log(BasicLevel.DEBUG,
00592                      "ATransaction, Delete (" + op.dirName + ',' + op.name + ')');
00593 
00594         File file;
00595         boolean deleted;
00596         if (op.dirName == null) {
00597           file = new File(dir, op.name);
00598           deleted = file.delete();
00599         } else {
00600           File parentDir = new File(dir, op.dirName);
00601           file = new File(parentDir, op.name);
00602           deleted = file.delete();
00603           deleteDir(parentDir);
00604         }
00605 
00606         if (!deleted && file.exists())
00607           logmon.log(BasicLevel.ERROR,
00608                      "ATransaction, can't delete " + file.getCanonicalPath());
00609       }
00610       op.free();
00611     }
00612     //  Be careful, do not clear log before all modifications are reported
00613     // to disk, in order to avoid load errors.
00614     log.clear();
00615 
00616     if (logmon.isLoggable(BasicLevel.DEBUG))
00617       logmon.log(BasicLevel.DEBUG, "ATransaction, Committed");
00618 
00619   }
00620 
00626   private final void deleteDir(File dir) {
00627     // Check the disk state. It may be false
00628     // according to the transaction log but
00629     // it doesn't matter because directories
00630     // are lazily created.
00631     String[] children = dir.list();
00632     // children may be null if dir doesn't exist any more.
00633     if (children != null && children.length == 0) {
00634       dir.delete();
00635       if (dir.getAbsolutePath().length() > 
00636       this.dir.getAbsolutePath().length()) {
00637         deleteDir(dir.getParentFile());
00638       }
00639     }
00640   }
00641 
00642   public final synchronized void _stop() {
00643     synchronized (lock) {
00644       while (phase != FREE) {
00645         // Wait for the transaction subsystem to be free
00646         try {
00647           wait();
00648         } catch (InterruptedException exc) {
00649         }
00650       }
00651       // Change the transaction state.
00652       setPhase(FINALIZE);
00653       isRunning =  false;
00654       garbage = true;
00655       // Wake-up the garbage thread.
00656       lock.notify();
00657     }
00658   }
00659 
00660   public final void stop() {
00661     if (logmon.isLoggable(BasicLevel.INFO))
00662       logmon.log(BasicLevel.INFO, "ATransaction, stops");
00663 
00664     _stop();
00665 
00666     try {
00667       // And waits for this thread to die.
00668       gThread.join();
00669     } catch (InterruptedException exc3) {
00670       if (logmon.isLoggable(BasicLevel.WARN))
00671         logmon.log(BasicLevel.WARN, "ATransaction, interrupted");
00672     }
00673 
00674     if (logmon.isLoggable(BasicLevel.INFO))
00675       logmon.log(BasicLevel.INFO, "ATransaction, stopped");
00676   }
00677 
00683   public void close() {
00684     stop();
00685   }
00686 
00687   public void run() {
00688     if (isRunning) return;
00689     isRunning = true;
00690 
00691     try {
00692       /*  If isRunning is false and garbage is true, the stop is arrived
00693        * during the previous garbage phase, so we have to garbaged the
00694        * last transactions. Normally, it should never happened because
00695        * we wait "Phase == Free" in stop().
00696        */
00697       while (isRunning || garbage) {
00698         synchronized (lock) {
00699           while (! garbage) {
00700             try {
00701               lock.wait();
00702             } catch (InterruptedException exc) {}
00703           }
00704           garbage = false;
00705         }
00706 
00707         wakeup();
00708       }
00709     } catch (IOException exc) {
00710       if (logmon.isLoggable(BasicLevel.ERROR))
00711         logmon.log(BasicLevel.ERROR, "ATransaction, tgarbage", exc);
00712       // TODO: ?
00713       exc.printStackTrace();
00714     } finally {
00715       isRunning = false;
00716 
00717       if (logmon.isLoggable(BasicLevel.DEBUG))
00718         logmon.log(BasicLevel.DEBUG, "ATransaction, ends");
00719 
00720       try {
00721         logFile.close();
00722       } catch (IOException exc) {
00723         logmon.log(BasicLevel.WARN, "ATransaction, can't close logfile", exc);
00724       }
00725 
00726       if (! lockFile.delete()) {
00727         logmon.log(BasicLevel.FATAL,
00728         "ATransaction, - can't delete lockfile.");
00729       }
00730 
00731       if (logmon.isLoggable(BasicLevel.INFO))
00732         logmon.log(BasicLevel.INFO, "ATransaction, exits.");
00733     }
00734   }
00735 
00736   private final void wakeup() throws IOException {
00737     if (logmon.isLoggable(BasicLevel.INFO))
00738       logmon.log(BasicLevel.INFO,
00739                  "ATransaction, Wakeup: " + commitCount + ", " +
00740                  operationCount + ", " + cumulativeSize);
00741     commitCount = operationCount = cumulativeSize = 0;
00742 
00743     Hashtable templog = plog;
00744     plog = clog;
00745     clog = templog;
00746 
00747     logFile.close();
00748 
00749     logFilePN.renameTo(plogFilePN);
00750     newLogFile();
00751 
00752     _release();
00753 
00754     commit(plog);
00755     // commit clears the log and frees Operations object.
00756     plogFilePN.delete();
00757 
00758     if (logmon.isLoggable(BasicLevel.INFO))
00759       logmon.log(BasicLevel.INFO, "ATransaction, Wakeup: end");
00760   }
00761 }
00762 
00763 final class ATOperation implements Serializable {
00764   static final int SAVE = 1;
00765   static final int DELETE = 2;
00766   static final int COMMIT = 3;
00767   static final int END = 127;
00768  
00769   int type;
00770   String dirName;
00771   String name;
00772   byte[] value;
00773 
00774   private ATOperation(int type, String dirName, String name, byte[] value) {
00775     this.type = type;
00776     this.dirName = dirName;
00777     this.name = name;
00778     this.value = value;
00779   }
00780 
00786   public String toString() {
00787     StringBuffer strbuf = new StringBuffer();
00788 
00789     strbuf.append('(').append(super.toString());
00790     strbuf.append(",type=").append(type);
00791     strbuf.append(",dirName=").append(dirName);
00792     strbuf.append(",name=").append(name);
00793     strbuf.append(')');
00794     
00795     return strbuf.toString();
00796   }
00797 
00798   private static Pool pool = null;
00799 
00800   static {
00801     pool = new Pool("Atransaction$ATOperation",
00802                     ATransaction.CLEANUP_THRESHOLD_OPERATION);
00803   }
00804 
00805   static ATOperation alloc(int type, String dirName, String name) {
00806     return alloc(type, dirName, name, null);
00807   }
00808 
00809   static ATOperation alloc(int type,
00810                          String dirName, String name,
00811                          byte[] value) {
00812     ATOperation op = null;
00813     
00814     try {
00815       op = (ATOperation) pool.allocElement();
00816     } catch (Exception exc) {
00817       return new ATOperation(type, dirName, name, value);
00818     }
00819     op.type = type;
00820     op.dirName = dirName;
00821     op.name = name;
00822     op.value = value;
00823     return op;
00824   }
00825 
00826   void free() {
00827     /* to let gc do its work */
00828     dirName = null;
00829     name = null;
00830     value = null;
00831     pool.freeElement(this);
00832   }
00833 }
00834 
00835 final class ATOperationKey {
00836   static Object newKey(String dirName, String name) {
00837     if (dirName == null) {
00838       return name;
00839     } else {
00840       return new ATOperationKey(dirName, name);
00841     }
00842   }
00843 
00844   private String dirName;
00845   private String name;
00846 
00847   private ATOperationKey(String dirName,
00848                        String name) {
00849     this.dirName = dirName;
00850     this.name = name;
00851   }
00852 
00853   public int hashCode() {
00854     // Should compute a specific one.
00855     return dirName.hashCode();
00856   }
00857 
00858   public boolean equals(Object obj) {
00859     if (this == obj) return true;
00860     if (obj instanceof ATOperationKey) {
00861       ATOperationKey opk = (ATOperationKey)obj;
00862       if (opk.name.length() != name.length()) return false;
00863       if (opk.dirName.length() != dirName.length()) return false;
00864       if (!opk.dirName.equals(dirName)) return false;            
00865       return opk.name.equals(name);
00866     }
00867     return false;
00868   }
00869 }

Generated on Tue Sep 16 16:14:30 2008 for joram by  doxygen 1.5.0