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();
+ }
+
+}