You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by gn...@apache.org on 2012/07/24 11:09:15 UTC

svn commit: r1364949 - in /aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb: ./ howl/ howl/log/ howl/log/Logger.java

Author: gnodet
Date: Tue Jul 24 09:09:15 2012
New Revision: 1364949

URL: http://svn.apache.org/viewvc?rev=1364949&view=rev
Log:
[ARIES-882] A thread is not stopped when the transaction manager is destroyed

Added:
    aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/
    aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/
    aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/
    aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/Logger.java

Added: aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/Logger.java
URL: http://svn.apache.org/viewvc/aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/Logger.java?rev=1364949&view=auto
==============================================================================
--- aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/Logger.java (added)
+++ aries/trunk/transaction/transaction-manager/src/main/java/org/objectweb/howl/log/Logger.java Tue Jul 24 09:09:15 2012
@@ -0,0 +1,559 @@
+/*
+ * JOnAS: Java(TM) Open Application Server
+ * Copyright (C) 2004 Bull S.A.
+ * All rights reserved.
+ * 
+ * Contact: howl@objectweb.org
+ * 
+ * This software is licensed under the BSD license.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *     
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *     
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * ------------------------------------------------------------------------------
+ * $Id: Logger.java,v 1.14 2006/04/21 15:03:36 girouxm Exp $
+ * ------------------------------------------------------------------------------
+ */
+package org.objectweb.howl.log;
+
+import java.io.IOException;
+
+/**
+ * Manage a configured set of two or more physical log files.
+ * 
+ * <p>Log files have a configured maximum size.  When a file has
+ * reached the configured capacity, Logger switches to
+ * the next available alternate file.  Normally, log files are created
+ * in advance to guarantee that space is available during execution.
+ * 
+ * <p>Each log file has a file header containing information 
+ * allowing Logger to reposition and replay the logs
+ * during a recovery scenario.
+ * 
+ * <p>LogFile <i> marking </i>
+ * <p>The LogFile's mark is the the position within the file
+ * of the oldest active entry.
+ * Initially the mark is set at the beginning of the file.
+ * At some configured interval, the caller invokes <i> mark() </i>
+ * with the key of the oldest active entry in the log.
+ * 
+ * <p>For XA the key would be for the oldest transaction still
+ * in committing state.  In theory, XA could call <i> mark() </i> every
+ * time a DONE record is logged.  In practice, it should only be
+ * necessary to call <i> mark() </i> every minute or so depending on the
+ * capacity of the log files.
+ * 
+ * <p>The Logger maintains an active mark within the set
+ * of log files.  A file may be reused only if the mark does not
+ * reside within the file.  The Logger will throw
+ * LogFileOverflowException if an attempt is made to switch to a
+ * file that contains a mark.
+ * 
+ * @author Michael Giroux
+ *
+ */
+public class Logger extends LogObject
+{
+  /**
+   * indicates whether the LogFile is open.
+   * <p>Logger methods return LogClosedException when log is closed.
+   */
+  protected volatile boolean isClosed = true;
+  
+  /**
+   * Manages a pool of buffers used for log file IO.
+   */
+  LogBufferManager bmgr = null;
+  
+  /**
+   * Manages a pool of files used for log file IO.
+   */
+  LogFileManager lfmgr = null;
+  
+  /**
+   * @return activeMark member of the associated LogFileManager.
+   */
+  public long getActiveMark()
+  {
+    return lfmgr.activeMark;
+  }
+  
+  /**
+   * Construct a Logger using default Configuration object.
+   * @throws IOException
+   */
+  public Logger()
+    throws IOException
+  {
+    this(new Configuration());
+  }
+  
+  /**
+   * Construct a Logger using a Configuration supplied
+   * by the caller.
+   * @param config Configuration object
+   * @throws IOException
+   */
+  public Logger(Configuration config)
+    throws IOException
+  {
+    super(config);
+
+    lfmgr = new LogFileManager(config);
+    
+    bmgr = new LogBufferManager(config);
+  }
+  
+  /**
+   * add a USER record consisting of byte[][] to log. 
+   * 
+   * <p>if <i> sync </i> parameter is true, then the method will
+   * block (in bmgr.put()) until the <i> data </i> buffer is forced to disk.
+   * Otherwise, the method returns immediately.
+   * 
+   * @param data record data
+   * @param sync true if call should block until force
+   * 
+   * @return a key that can be used to locate the record.
+   * Some implementations may use the key as a correlation ID 
+   * to associate related records.
+   * 
+   * When automark is disabled (false) the caller must
+   * invoke mark() using this key to indicate the location
+   * of the oldest active entry in the log.
+   * 
+   * @throws LogClosedException
+   * @throws LogRecordSizeException
+   * @throws LogFileOverflowException
+   * @throws InterruptedException
+   * @throws IOException
+   * 
+   * @see #mark(long)
+   * @see #setAutoMark(boolean)
+   */
+  public long put(byte[][] data, boolean sync)
+    throws LogClosedException, LogRecordSizeException, LogFileOverflowException,
+                InterruptedException, IOException
+  {
+    return put(LogRecordType.USER, data, sync);
+  }
+  
+  /**
+   * add a USER record consisting of byte[] to the log.
+   * 
+   * <p>wrap byte[] <i> data </i> in a new byte[][]
+   * and delegates call to put(byte[][], boolean)
+   *  
+   * @param data byte[] to be written to log
+   * @param sync true if caller wishes to block waiting for the
+   * record to force to disk.
+   * @return log key for the record
+   * @throws LogClosedException
+   * @throws LogRecordSizeException
+   * @throws LogFileOverflowException
+   * @throws InterruptedException
+   * @throws IOException
+   */
+  public long put(byte[] data, boolean sync)
+    throws LogClosedException, LogRecordSizeException, LogFileOverflowException,
+      InterruptedException, IOException
+  {
+    return put(LogRecordType.USER, new byte[][]{data}, sync);
+  }
+
+  /**
+   * Sub-classes call this method to write log records with
+   * a specific record type.
+   * 
+   * @param type a record type defined in LogRecordType.
+   * @param data record data to be logged.
+   * @param sync boolean indicating whether call should
+   * wait for data to be written to physical disk.
+   * 
+   * @return a log key that can be used to reference
+   * the record.
+   */
+  protected long put(short type, byte[][] data, boolean sync)
+  throws LogClosedException, LogRecordSizeException, LogFileOverflowException,
+  InterruptedException, IOException
+  {
+    synchronized(this)
+    {
+      if (isClosed) throw new LogClosedException();
+    }
+    
+    // QUESTION: should we deal with exceptions here?
+
+    long key = bmgr.put(type, data, sync);
+    lfmgr.setCurrentKey(key);
+    
+    return key;
+  }
+  
+  /**
+   * sets the LogFile's mark.
+   * 
+   * <p><i> mark() </i> provides a generalized method for callers
+   * to inform the Logger that log space can be released
+   * for reuse.
+   * 
+   * <p>calls LogFileManager to process the request.
+   * 
+   * @param key is a log key returned by a previous call to put().
+   * @param force a boolean that indicates whether the mark data
+   * should be forced to disk.  When set to <b> true </b> the caller
+   * is blocked until the mark record is forced to disk.
+   * 
+   * @throws InvalidLogKeyException
+   * if <i> key </i> parameter is out of range.
+   * <i> key </i> must be greater than current activeMark and less than the most recent
+   * key returned by put().
+   * @throws LogClosedException
+   * if this logger instance has been closed.
+   */
+  public void mark(long key, boolean force)
+    throws InvalidLogKeyException, LogClosedException, IOException, InterruptedException
+  {
+    synchronized(this)
+    {
+      if (isClosed)
+        throw new LogClosedException("log is closed");
+    }
+    
+    lfmgr.mark(key, force);
+  }
+  
+  /**
+   * calls Logger.mark(key, force) with <i> force </i> set to <b> true </b>.
+   * <p>Caller is blocked until mark record is forced to disk.
+   * @param key a log key returned by a previous call to put().
+   * @throws InvalidLogKeyException
+   * @throws LogClosedException
+   * @throws IOException
+   * @throws InterruptedException
+   * @see #mark(long, boolean)
+   */
+  public void mark(long key)
+    throws InvalidLogKeyException, LogClosedException, IOException, InterruptedException
+  {
+    mark(key, true);
+  }
+  
+  /**
+   * Sets the LogFile marking mode.
+   * 
+   * <p>passes call to LogFileManager
+   * 
+   * @param autoMark true to indicate automatic marking.
+   */
+  public void setAutoMark(boolean autoMark)
+    throws InvalidLogKeyException, LogClosedException, LogFileOverflowException, IOException, InterruptedException
+  {
+    synchronized(this)
+    {
+      if (this.isClosed) throw new LogClosedException();
+    }
+    
+    lfmgr.setAutoMark(autoMark);
+  }
+  
+  /**
+   * close the Log files and perform necessary cleanup tasks.
+   */
+  public void close() throws IOException, InterruptedException
+  {
+    // prevent new threads from adding to the log
+    synchronized(this) { isClosed = true; }
+    
+    lfmgr.close();
+    bmgr.close();
+  }
+  
+  /**
+   * open Log files and perform necessart initialization.
+   * 
+   * TODO: consider open(String name) to allow named configurations.
+   *       this would allow utility to open two loggers and copy 
+   *       old records to new files.
+   *
+   */
+  public void open()
+    throws InvalidFileSetException,
+           IOException, LogConfigurationException, InvalidLogBufferException, InterruptedException
+  {
+    lfmgr.open();
+    
+    try {
+      bmgr.open();
+    } catch (ClassNotFoundException e) {
+      String cnf = "LogBuffer Class not found: " + config.getBufferClassName();
+      LogConfigurationException lce = new LogConfigurationException(cnf, e);
+      throw lce;
+    }
+    
+    // read header information from each file
+    lfmgr.init(bmgr);
+    
+    // indicate that Log is ready for use.
+    synchronized(this) { isClosed = false; }
+  }
+  
+  /**
+   * Registers a LogEventListener for log event notifications.
+   * 
+   * @param eventListener object to be notified of logger events.
+   */
+  public void setLogEventListener(LogEventListener eventListener)
+  {
+    lfmgr.setLogEventListener(eventListener);
+  }
+  
+  /**
+   * Replays log from a specified mark forward to the current mark.
+   * 
+   * <p>Beginning with the record located at <i> mark </i>
+   * the Logger reads log records forward to the end of the log.
+   * USER records are passed to the <i> listener </i> onRecord()
+   * method. When the end of log has been reached, replay returns
+   * one final record with a type of END_OF_LOG to inform <i> listener </i>
+   * that no further records will be returned.
+   * 
+   * <p>If an error is encountered while reading the log, the
+   * <i> listener </i> onError method is called.  Replay terminates
+   * when any error occurs and when END_OF_LOG is encountered.
+   * 
+   * @param listener an object that implements ReplayListener interface.
+   * @param mark a log key to begin replay from.
+   * <p>The <i> mark </i> should be a valid log key returned by the put()
+   * method.  To replay the entire log beginning with the oldest available
+   * record, <i> mark </i> should be set to zero (0L).
+   * @throws LogConfigurationException
+   * most likely because the configured LogBuffer class cannot be found.
+   * @throws InvalidLogKeyException
+   * if <i> mark </i> is not a valid log key.
+   */
+  public void replay(ReplayListener listener, long mark)
+    throws InvalidLogKeyException, LogConfigurationException
+  {
+    // replay only the user records.
+    bmgr.replay(listener, mark, false);
+  }
+  
+  /**
+   * Replays log from the active mark forward to the current position.
+   * 
+   * @param listener an object that implements ReplayListener interface.
+   * @throws LogConfigurationException
+   * most likely because the configured LogBuffer class cannot be found.
+   * @see #replay(ReplayListener, long)
+   */
+  public void replay(ReplayListener listener) throws LogConfigurationException
+  {
+    try {
+      bmgr.replay(listener, lfmgr.activeMark, false);
+    } catch (InvalidLogKeyException e) {
+      // should not happen -- use assert to catch during development
+      assert false : "Unhandled InvalidLogKeyException" + e.toString();
+    }
+  }
+  
+  /**
+   * Allows sub-classes of Logger to replay control records.
+   * 
+   * @param listener ReplayListener to receive the records 
+   * @param mark starting mark (log key) for the replay.
+   * @param replayCtrlRecords boolean indicating whether to
+   * return CTRL records.
+   * @throws InvalidLogKeyException
+   * If the <i> mark </i> parameter specifies an invalid log key
+   * (one that does not exist in the current set of log files.)
+   * @throws LogConfigurationException
+   * 
+   * @see org.objectweb.howl.log.LogBufferManager#replay(ReplayListener, long, boolean)
+   */
+  protected void replay(ReplayListener listener, long mark, boolean replayCtrlRecords)
+  throws InvalidLogKeyException, LogConfigurationException
+  {
+    bmgr.replay(listener, mark, replayCtrlRecords);
+  }
+  
+  /**
+   * Read a specific record from the log.
+   * <p>Control records are not filtered by this method.
+   * If the requested mark is valid and identifies a control record,
+   * the record will be returned.
+   * @param lr LogRecord to be updated or null if caller wishes a new
+   *           LogRecord to be allocated.
+   * @param mark a log key identifying the location of the record
+   *               within the journal
+   * @return LogRecord containing requested record
+   * @throws InvalidLogKeyException
+   *         if logkey parameter is < 0L or if the requested key is not within the current range
+   *         of keys in the log.
+   * @throws LogConfigurationException
+   * @throws LogException
+   */  // FEATURE: 300792
+  public LogRecord get(LogRecord lr, long mark) throws InvalidLogKeyException,
+    LogConfigurationException,
+    LogException, InvalidLogBufferException
+  {
+    /* this code is similar to LogBufferManager.replay() -- potential for refactor */
+    int bsn = bmgr.bsnFromMark(mark);
+    if (mark < 0 || (bsn == 0 && mark != 0))
+      throw new InvalidLogKeyException(Long.toHexString(mark));
+    
+    if (lr == null)
+      lr = new LogRecord((config.getBufferSize() * 1024)/4); // default to 1/4 buffer size
+    
+    // allocate a LogBuffer that we can use to read the journal
+    try {
+      if (lr.buffer == null)
+        lr.buffer = bmgr.getLogBuffer(-1);
+    } catch (ClassNotFoundException e) {
+      throw new LogConfigurationException(e);
+    }
+    
+    LogBuffer buffer = lr.buffer;
+    
+    // read block containing requested mark
+    try {
+      bmgr.forceCurrentBuffer();
+      lfmgr.read(buffer, bsn);
+    } catch (IOException e) {
+      LogFile lf = buffer.lf;
+      String msg = "Error reading " + lf.file + " @ position [" + lf.position + "]";
+      throw new LogException(msg, e);
+    }
+    
+    if (buffer.bsn == -1)
+    {
+      lr.type = LogRecordType.END_OF_LOG;
+      return lr;
+    }
+    
+    // verify we have the desired block
+    // if requested mark == 0 then we start with the oldest block available
+    int markBSN = (mark == 0) ? buffer.bsn : bmgr.bsnFromMark(mark);
+    if (markBSN != buffer.bsn) {
+      InvalidLogBufferException lbe = new InvalidLogBufferException(
+          "block read [" + buffer.bsn + "] not block requested: " + markBSN);
+      throw lbe;
+    }
+    
+    /*
+     * position buffer to requested mark.
+     * 
+     * Although the mark contains a buffer offset, we search forward
+     * through the buffer to guarantee that we have the start
+     * of a record.  This protects against using marks that were
+     * not generated by the current Logger.
+     */
+    lr.get(buffer); // get first record in buffer
+    if (mark > 0 && mark > bmgr.markFromBsn(markBSN,0)) {
+      while(lr.key < mark) {
+        lr.get(buffer);
+      }
+      if (lr.key != mark) {
+        String msg = "The requested mark [" + Long.toHexString(mark) + 
+          "] was not found in the log.";
+        // BUG 300733 following line changed to throw an exception
+        throw new InvalidLogKeyException(msg);
+      }
+    }
+    
+    return lr;
+  }
+  
+  /**
+   * Read the journal record that follows the record identified by lr.
+   * @param lr LogRecord to be updated with the next journal record.
+   * <p>The LogRecord <code> lr </code> must have been returned
+   * by a previous call to Logger.get(LogRecord, long). 
+   * <p>Effectively, the record identified by lr is located, and the record
+   * immediately following it is returned.
+   * @return LogRecord containing the requested record.
+   * @throws IllegalArgumentException
+   * if lr parameter is null or if the lr.buffer member is null.
+   */  // FEATURE: 300792
+  public LogRecord getNext(LogRecord lr)
+    throws InvalidLogBufferException, LogException
+  {
+    if (lr == null || lr.buffer == null) throw new IllegalArgumentException();
+    
+    LogBuffer buffer = lr.buffer;
+    
+    // get next record
+    lr.get(buffer);
+    
+    if (lr.isEOB())
+    {
+      long bsn = buffer.bsn;  // so we can test for wraparound
+      try {
+        lfmgr.read(buffer, buffer.bsn+1);
+      } catch (IOException e) {
+        LogFile lf = lr.buffer.lf;
+        String msg = "Error reading " + lf.file + " @ position [" + lf.position + "]";
+        throw new LogException(msg, e);
+      }
+      
+      if (buffer.bsn == -1 || buffer.bsn < bsn) // BUG 304982
+      {
+        lr.type = LogRecordType.END_OF_LOG;
+        return lr;
+      }
+      
+      lr.get(buffer);
+    }
+
+    return lr;
+  }
+
+  /**
+   * return an XML node containing statistics for the Logger,
+   * the LogFile pool and the LogBuffer pool.
+   * 
+   * <p>The getStats method for the LogBufferManager and LogFileManager
+   * are called to include statistics for these contained objects.
+   * 
+   * @return String contiining XML node.
+   */
+  public String getStats()
+  {
+    String name = this.getClass().getName();
+    StringBuffer stats = new StringBuffer(
+        "<Logger  class='" + name + "'>" 
+    );
+    
+    // TODO: append Logger specific stats
+    
+    stats.append(bmgr.getStats());
+    
+    stats.append(lfmgr.getStats());
+    
+    stats.append("\n</Logger>" +
+        "\n");
+    
+    return stats.toString();
+  }
+  
+}