ASF Bugzilla – Attachment 19004 Details for
Bug 24159
Log4J can create deadlock conditions (concurrent package donation)
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
RollingFileAppender and ReentrantReadWriteLock etc.
concurrent.diff (text/plain), 76.57 KB, created by
Elias Ross
on 2006-10-13 12:09:58 UTC
(
hide
)
Description:
RollingFileAppender and ReentrantReadWriteLock etc.
Filename:
MIME Type:
Creator:
Elias Ross
Created:
2006-10-13 12:09:58 UTC
Size:
76.57 KB
patch
obsolete
>Index: src/java/org/apache/log4j/concurrent/ReadWriteLock.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/ReadWriteLock.java (revision 0) >+++ src/java/org/apache/log4j/concurrent/ReadWriteLock.java (revision 0) >@@ -0,0 +1,82 @@ >+/* >+ File: ReadWriteLock.java >+ >+ Originally written by Doug Lea and released into the public domain. >+ This may be used for any purposes whatsoever without acknowledgment. >+ Thanks for the assistance and support of Sun Microsystems Labs, >+ and everyone contributing, testing, and using this code. >+ >+ History: >+ Date Who What >+ 11Jun1998 dl Create public version >+*/ >+ >+ >+package org.apache.log4j.concurrent; >+ >+ >+/** >+ * ReadWriteLocks maintain a pair of associated locks. >+ * The readLock may be held simultanously by multiple >+ * reader threads, so long as there are no writers. The writeLock >+ * is exclusive. ReadWrite locks are generally preferable to >+ * plain Sync locks or synchronized methods in cases where: >+ * <ul> >+ * <li> The methods in a class can be cleanly separated into >+ * those that only access (read) data vs those that >+ * modify (write). >+ * <li> Target applications generally have more readers than writers. >+ * <li> The methods are relatively time-consuming (as a rough >+ * rule of thumb, exceed more than a hundred instructions), so it >+ * pays to introduce a bit more overhead associated with >+ * ReadWrite locks compared to simple synchronized methods etc >+ * in order to allow concurrency among reader threads. >+ * >+ * </ul> >+ * Different implementation classes differ in policies surrounding >+ * which threads to prefer when there is >+ * contention. By far, the most commonly useful policy is >+ * WriterPreferenceReadWriteLock. The other implementations >+ * are targeted for less common, niche applications. >+ *<p> >+ * Standard usage: >+ * <pre> >+ * class X { >+ * ReadWriteLock rw; >+ * // ... >+ * >+ * public void read() throws InterruptedException { >+ * rw.readLock().acquire(); >+ * try { >+ * // ... do the read >+ * } >+ * finally { >+ * rw.readlock().release() >+ * } >+ * } >+ * >+ * >+ * public void write() throws InterruptedException { >+ * rw.writeLock().acquire(); >+ * try { >+ * // ... do the write >+ * } >+ * finally { >+ * rw.writelock().release() >+ * } >+ * } >+ * } >+ * </pre> >+ * @see Sync >+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] >+ >+**/ >+ >+public interface ReadWriteLock { >+ /** get the readLock **/ >+ Sync readLock(); >+ >+ /** get the writeLock **/ >+ Sync writeLock(); >+} >+ > >Property changes on: src/java/org/apache/log4j/concurrent/ReadWriteLock.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: src/java/org/apache/log4j/concurrent/ConcurrentAppender.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/ConcurrentAppender.java (revision 463475) >+++ src/java/org/apache/log4j/concurrent/ConcurrentAppender.java (working copy) >@@ -19,7 +19,6 @@ > import org.apache.log4j.Layout; > import org.apache.log4j.Appender; > import org.apache.log4j.Priority; >-import org.apache.log4j.helpers.ReaderWriterLock; > import org.apache.log4j.spi.ComponentBase; > import org.apache.log4j.spi.Filter; > import org.apache.log4j.spi.LoggingEvent; >@@ -38,12 +37,14 @@ > * method or within their own {@link #append} method. > * </p> > * <p> >+ * If Thread.interrupt() is called during append of the logger thread, >+ * the customary behavior in this package is to stop appending. >+ * </p> >+ * <p> > * This class is heavily based on the {@link > * #org.apache.log4j.AppenderSkeleton} class. It may be a useful base class > * for creating appenders that can benefit from concurrent I/O access. > * </p> >- * >- * @see #getWriteLock > */ > public abstract class ConcurrentAppender > extends ComponentBase implements Appender, OptionHandler >@@ -90,10 +91,10 @@ > /** > * A write lock is obtained to change options, a read lock is obtained to > * append events. >+ * This is a re-entrant writer-preference read-write lock. > */ >- private ReaderWriterLock lock = new ReaderWriterLock(); >+ protected ReadWriteLock lock = new ReentrantWriterPreferenceReadWriteLock(); > >- > /** > * Constructs a ConcurrentAppender. > * >@@ -169,7 +170,7 @@ > */ > public boolean isAsSevereAsThreshold(final Priority level) { > Priority copy = threshold; >- return ((copy == null) || copy.isGreaterOrEqual(level)); >+ return ((copy == null) || level.isGreaterOrEqual(copy)); > } > > /** >@@ -195,7 +196,7 @@ > guard.set(this); // arbitrary thread lock object > try { > >- lock.getReadLock(); >+ lock.readLock().acquire(); > try { > > >@@ -214,9 +215,11 @@ > append(event); > > } finally { >- lock.releaseReadLock(); >+ lock.readLock().release(); > } > >+ } catch (InterruptedException e) { >+ getLogger().info("interrupted", e); > } finally { > guard.set(null); > } >@@ -272,14 +275,20 @@ > * Calls {@link #internalClose} when completed. > * Implementation note: Obtains a write lock before starting close. > * Calling this method more than once does nothing. >+ * @throws RuntimeException if the thread is interrupted > */ > public final void close() { > boolean wasClosed; >- getWriteLock(); >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return; >+ } > try { > wasClosed = closed.set(true); > } finally { >- lock.releaseWriteLock(); >+ lock.writeLock().release(); > } > > if (!wasClosed) >@@ -334,35 +343,6 @@ > protected abstract void internalClose(); > > /** >- * Obtains a write lock that blocks logging to {@link #append}. >- * This is normally done when changing output behavior, closing and reopening >- * streams, etc. Call {@link #releaseWriteLock} to resume logging. >- * <p> >- * Usage pattern: >- <pre> >- getWriteLock(); >- try { >- // ... >- } finally { >- releaseWriteLock(); >- } >- </pre> >- * Note: Do not attempt to re-acquire this lock. This lock should only be >- * used for critical sections and not for long periods of time. >- */ >- protected void getWriteLock() { >- lock.getWriteLock(); >- } >- >- /** >- * Releases a write lock; allows calls to the {@link #append} method. >- * @see #getWriteLock >- */ >- protected void releaseWriteLock() { >- lock.releaseWriteLock(); >- } >- >- /** > * Finalizes this appender by calling this {@link #close} method. > */ > protected void finalize() { >@@ -424,7 +404,7 @@ > if (f != null) > sb.append(','); > } >- return f.toString(); >+ return sb.toString(); > } > > } >Index: src/java/org/apache/log4j/concurrent/RollingFileAppender.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/RollingFileAppender.java (revision 0) >+++ src/java/org/apache/log4j/concurrent/RollingFileAppender.java (revision 0) >@@ -0,0 +1,530 @@ >+/* >+ * Copyright 1999,2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import org.apache.log4j.rolling.RollingPolicy; >+import org.apache.log4j.rolling.RolloverDescription; >+import org.apache.log4j.rolling.TriggeringPolicy; >+import org.apache.log4j.rolling.helper.Action; >+import org.apache.log4j.spi.LoggingEvent; >+ >+import java.io.File; >+import java.io.FileOutputStream; >+import java.io.IOException; >+import java.io.OutputStream; >+import java.io.OutputStreamWriter; >+import java.io.Writer; >+ >+ >+/** >+ * <code>RollingFileAppender</code> extends {@link FileAppender} to backup the log files >+ * depending on {@link RollingPolicy} and {@link TriggeringPolicy}. >+ * <p> >+ * To be of any use, a <code>RollingFileAppender</code> instance must have both >+ * a <code>RollingPolicy</code> and a <code>TriggeringPolicy</code> set up. >+ * However, if its <code>RollingPolicy</code> also implements the >+ * <code>TriggeringPolicy</code> interface, then only the former needs to be >+ * set up. For example, {@link TimeBasedRollingPolicy} acts both as a >+ * <code>RollingPolicy</code> and a <code>TriggeringPolicy</code>. >+ * >+ * <p><code>RollingFileAppender</code> can be configured programattically or >+ * using {@link org.apache.log4j.joran.JoranConfigurator}. Here is a sample >+ * configration file: >+ >+<pre><?xml version="1.0" encoding="UTF-8" ?> >+<!DOCTYPE log4j:configuration> >+ >+<log4j:configuration debug="true"> >+ >+ <appender name="ROLL" class="org.apache.log4j.rolling.RollingFileAppender"> >+ <b><rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy"> >+ <param name="FileNamePattern" value="/wombat/foo.%d{yyyy-MM}.gz"/> >+ </rollingPolicy></b> >+ >+ <layout class="org.apache.log4j.PatternLayout"> >+ <param name="ConversionPattern" value="%c{1} - %m%n"/> >+ </layout> >+ </appender> >+ >+ <root"> >+ <appender-ref ref="ROLL"/> >+ </root> >+ >+</log4j:configuration> >+</pre> >+ >+ *<p>This configuration file specifies a monthly rollover schedule including >+ * automatic compression of the archived files. See >+ * {@link TimeBasedRollingPolicy} for more details. >+ * >+ * @author Heinz Richter >+ * @author Ceki Gülcü >+ * @since 1.3 >+ * */ >+public final class RollingFileAppender extends FileAppender { >+ /** >+ * Triggering policy. >+ */ >+ private TriggeringPolicy triggeringPolicy; >+ >+ /** >+ * Rolling policy. >+ */ >+ private RollingPolicy rollingPolicy; >+ >+ /** >+ * Length of current active log file. >+ */ >+ private long fileLength = 0; >+ >+ /** >+ * Lock to protect the length. >+ */ >+ private Object fileLengthLock = new Object(); >+ >+ /** >+ * Asynchronous action (like compression) from previous rollover. >+ */ >+ private Action lastRolloverAsyncAction = null; >+ >+ /** >+ * Construct a new instance. >+ */ >+ public RollingFileAppender() { >+ } >+ >+ /** >+ * Prepare instance of use. >+ */ >+ public void activateOptions() { >+ if (rollingPolicy == null) { >+ getLogger().warn( >+ "Please set a rolling policy for the RollingFileAppender named '{}'", >+ getName()); >+ >+ return; >+ } >+ >+ // >+ // if no explicit triggering policy and rolling policy is both. >+ // >+ if ( >+ (triggeringPolicy == null) && rollingPolicy instanceof TriggeringPolicy) { >+ triggeringPolicy = (TriggeringPolicy) rollingPolicy; >+ } >+ >+ if (triggeringPolicy == null) { >+ getLogger().warn( >+ "Please set a TriggeringPolicy for the RollingFileAppender named '{}'", >+ getName()); >+ >+ return; >+ } >+ >+ Exception exception = null; >+ >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return; >+ } >+ try { >+ triggeringPolicy.activateOptions(); >+ rollingPolicy.activateOptions(); >+ >+ try { >+ RolloverDescription rollover = >+ rollingPolicy.initialize(getFile(), getAppend()); >+ >+ if (rollover != null) { >+ Action syncAction = rollover.getSynchronous(); >+ >+ if (syncAction != null) { >+ syncAction.execute(); >+ } >+ >+ setFile(rollover.getActiveFileName()); >+ setAppend(rollover.getAppend()); >+ lastRolloverAsyncAction = rollover.getAsynchronous(); >+ >+ if (lastRolloverAsyncAction != null) { >+ Thread t = new Thread(lastRolloverAsyncAction); >+ t.setName("log4j-rollover-" + getName()); >+ t.start(); >+ } >+ } >+ >+ File activeFile = new File(getFile()); >+ >+ if (getAppend()) { >+ setFileLength(activeFile.length()); >+ } else { >+ setFileLength(0); >+ } >+ >+ super.activateOptions(); >+ } catch (Exception ex) { >+ exception = ex; >+ } >+ } finally { >+ lock.writeLock().release(); >+ } >+ >+ if (exception != null) { >+ getLogger().warn( >+ "Exception while initializing RollingFileAppender named '" + getName() >+ + "'", exception); >+ } >+ } >+ >+ /** >+ Implements the usual roll over behaviour. >+ >+ <p>If <code>MaxBackupIndex</code> is positive, then files >+ {<code>File.1</code>, ..., <code>File.MaxBackupIndex -1</code>} >+ are renamed to {<code>File.2</code>, ..., >+ <code>File.MaxBackupIndex</code>}. Moreover, <code>File</code> is >+ renamed <code>File.1</code> and closed. A new <code>File</code> is >+ created to receive further log output. >+ >+ <p>If <code>MaxBackupIndex</code> is equal to zero, then the >+ <code>File</code> is truncated with no backup files created. >+ >+ * @return true if rollover performed. >+ */ >+ public boolean rollover() { >+ // >+ // can't roll without a policy >+ // >+ if (rollingPolicy != null) { >+ Exception exception = null; >+ >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return false; >+ } >+ try { >+ // >+ // if a previous async task is still running >+ // >+ if (lastRolloverAsyncAction != null) { >+ // >+ // block until complete >+ // >+ lastRolloverAsyncAction.close(); >+ >+ // >+ // or don't block and return to rollover later >+ // >+ //if (!lastRolloverAsyncAction.isComplete()) return false; >+ } >+ >+ try { >+ RolloverDescription rollover = rollingPolicy.rollover(getFile()); >+ >+ if (rollover != null) { >+ if (rollover.getActiveFileName().equals(getFile())) { >+ closeWriter(); >+ >+ boolean success = true; >+ >+ if (rollover.getSynchronous() != null) { >+ success = false; >+ >+ try { >+ success = rollover.getSynchronous().execute(); >+ } catch (Exception ex) { >+ exception = ex; >+ } >+ } >+ >+ if (success) { >+ if (rollover.getAppend()) { >+ setFileLength(new File(rollover.getActiveFileName()).length()); >+ } else { >+ setFileLength(0); >+ } >+ >+ if (rollover.getAsynchronous() != null) { >+ lastRolloverAsyncAction = rollover.getAsynchronous(); >+ Thread t = new Thread(lastRolloverAsyncAction); >+ t.setName("log4j-rollover-" + getName()); >+ t.start(); >+ } >+ >+ setFile( >+ rollover.getActiveFileName(), rollover.getAppend(), >+ bufferedIO, bufferSize); >+ } else { >+ setFile( >+ rollover.getActiveFileName(), true, bufferedIO, bufferSize); >+ >+ if (exception == null) { >+ getLogger().warn("Failure in post-close rollover action"); >+ } else { >+ getLogger().warn( >+ "Exception in post-close rollover action", exception); >+ } >+ } >+ } else { >+ Writer newWriter = >+ createWriter( >+ new FileOutputStream( >+ rollover.getActiveFileName(), rollover.getAppend())); >+ closeWriter(); >+ setFile(rollover.getActiveFileName()); >+ setWriter(newWriter); >+ >+ boolean success = true; >+ >+ if (rollover.getSynchronous() != null) { >+ success = false; >+ >+ try { >+ success = rollover.getSynchronous().execute(); >+ } catch (Exception ex) { >+ exception = ex; >+ } >+ } >+ >+ if (success) { >+ if (rollover.getAppend()) { >+ fileLength = new File(rollover.getActiveFileName()).length(); >+ } else { >+ fileLength = 0; >+ } >+ >+ if (rollover.getAsynchronous() != null) { >+ Thread t = new Thread(lastRolloverAsyncAction); >+ t.setName("log4j-rollover-" + getName()); >+ t.start(); >+ } >+ } >+ >+ writeHeader(); >+ } >+ >+ return true; >+ } >+ } catch (Exception ex) { >+ exception = ex; >+ } >+ } finally { >+ lock.writeLock().release(); >+ } >+ >+ if (exception != null) { >+ getLogger().warn( >+ "Exception during rollover, rollover deferred.", exception); >+ } >+ } >+ >+ return false; >+ } >+ >+ private boolean isTriggeringEvent(LoggingEvent event) { >+ return triggeringPolicy.isTriggeringEvent( >+ this, event, getFile(), getFileLength()); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ protected void subAppend(final LoggingEvent event) { >+ // The rollover check must precede actual writing. This is the >+ // only correct behavior for time driven triggers. >+ if (isTriggeringEvent(event)) { >+ >+ // upgrade the lock >+ lock.readLock().release(); >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return; >+ } >+ try { >+ if (isTriggeringEvent(event)) { >+ rollover(); >+ } >+ } finally { >+ // downgrade the lock >+ try { >+ lock.readLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return; >+ } >+ lock.writeLock().release(); >+ } >+ } >+ >+ super.subAppend(event); >+ } >+ >+ /** >+ * Get rolling policy. >+ * @return rolling policy. >+ */ >+ public RollingPolicy getRollingPolicy() { >+ return rollingPolicy; >+ } >+ >+ /** >+ * Get triggering policy. >+ * @return triggering policy. >+ */ >+ public TriggeringPolicy getTriggeringPolicy() { >+ return triggeringPolicy; >+ } >+ >+ /** >+ * Sets the rolling policy. >+ * @param policy rolling policy. >+ */ >+ public void setRollingPolicy(final RollingPolicy policy) { >+ rollingPolicy = policy; >+ } >+ >+ /** >+ * Set triggering policy. >+ * @param policy triggering policy. >+ */ >+ public void setTriggeringPolicy(final TriggeringPolicy policy) { >+ triggeringPolicy = policy; >+ } >+ >+ /** >+ * Close appender. Waits for any asynchronous file compression actions to be completed. >+ */ >+ public void internalClose() { >+ if (lastRolloverAsyncAction != null) { >+ lastRolloverAsyncAction.close(); >+ } >+ >+ super.internalClose(); >+ } >+ >+ /** >+ Returns an OutputStreamWriter when passed an OutputStream. The >+ encoding used will depend on the value of the >+ <code>encoding</code> property. If the encoding value is >+ specified incorrectly the writer will be opened using the default >+ system encoding (an error message will be printed to the loglog. >+ @param os output stream, may not be null. >+ @return new writer. >+ */ >+ protected OutputStreamWriter createWriter(final OutputStream os) { >+ return super.createWriter(new CountingOutputStream(os, this)); >+ } >+ >+ /** >+ * Get byte length of current active log file. >+ * @return byte length of current active log file. >+ */ >+ public long getFileLength() { >+ synchronized (fileLengthLock) { >+ return fileLength; >+ } >+ } >+ >+ private void setFileLength(long len) { >+ synchronized (fileLengthLock) { >+ fileLength = len; >+ } >+ } >+ >+ /** >+ * Increments estimated byte length of current active log file. >+ * @param increment additional bytes written to log file. >+ */ >+ public void incrementFileLength(int increment) { >+ synchronized (fileLengthLock) { >+ fileLength += increment; >+ } >+ } >+ >+ /** >+ * Wrapper for OutputStream that will report all write >+ * operations back to this class for file length calculations. >+ */ >+ static class CountingOutputStream extends OutputStream { >+ /** >+ * Wrapped output stream. >+ */ >+ private final OutputStream os; >+ >+ /** >+ * Rolling file appender to inform of stream writes. >+ */ >+ private final RollingFileAppender rfa; >+ >+ /** >+ * Constructor. >+ * @param os output stream to wrap. >+ * @param rfa rolling file appender to inform. >+ */ >+ public CountingOutputStream( >+ final OutputStream os, final RollingFileAppender rfa) { >+ this.os = os; >+ this.rfa = rfa; >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void close() throws IOException { >+ os.close(); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void flush() throws IOException { >+ os.flush(); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void write(final byte[] b) throws IOException { >+ os.write(b); >+ rfa.incrementFileLength(b.length); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void write(final byte[] b, final int off, final int len) >+ throws IOException { >+ os.write(b, off, len); >+ rfa.incrementFileLength(len); >+ } >+ >+ /** >+ * {@inheritDoc} >+ */ >+ public void write(final int b) throws IOException { >+ os.write(b); >+ rfa.incrementFileLength(1); >+ } >+ } >+} >+ > >Property changes on: src/java/org/apache/log4j/concurrent/RollingFileAppender.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: src/java/org/apache/log4j/concurrent/WriterAppender.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/WriterAppender.java (revision 463475) >+++ src/java/org/apache/log4j/concurrent/WriterAppender.java (working copy) >@@ -145,11 +145,15 @@ > * Close the underlying {@link java.io.Writer}. > */ > protected void closeWriter() { >- getWriteLock(); >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ } > try { > closeWriter0(); > } finally { >- releaseWriteLock(); >+ lock.writeLock().release(); > } > } > >@@ -218,13 +222,18 @@ > @param writer An already opened Writer. */ > public void setWriter(Writer writer) { > // close any previously opened writer >- getWriteLock(); >+ try { >+ lock.writeLock().acquire(); >+ } catch (InterruptedException e) { >+ getLogger().warn("interrupted", e); >+ return; >+ } > try { > closeWriter0(); > this.writer = writer; > writeHeader(); > } finally { >- releaseWriteLock(); >+ lock.writeLock().release(); > } > } > >Index: src/java/org/apache/log4j/concurrent/WriterPreferenceReadWriteLock.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/WriterPreferenceReadWriteLock.java (revision 0) >+++ src/java/org/apache/log4j/concurrent/WriterPreferenceReadWriteLock.java (revision 0) >@@ -0,0 +1,312 @@ >+/* >+ File: WriterPreferenceReadWriteLock.java >+ >+ Originally written by Doug Lea and released into the public domain. >+ This may be used for any purposes whatsoever without acknowledgment. >+ Thanks for the assistance and support of Sun Microsystems Labs, >+ and everyone contributing, testing, and using this code. >+ >+ History: >+ Date Who What >+ 11Jun1998 dl Create public version >+ 5Aug1998 dl replaced int counters with longs >+ 25aug1998 dl record writer thread >+ 3May1999 dl add notifications on interrupt/timeout >+ >+*/ >+ >+package org.apache.log4j.concurrent; >+ >+/** >+ * A ReadWriteLock that prefers waiting writers over >+ * waiting readers when there is contention. This class >+ * is adapted from the versions described in CPJ, improving >+ * on the ones there a bit by segregating reader and writer >+ * wait queues, which is typically more efficient. >+ * <p> >+ * The locks are <em>NOT</em> reentrant. In particular, >+ * even though it may appear to usually work OK, >+ * a thread holding a read lock should not attempt to >+ * re-acquire it. Doing so risks lockouts when there are >+ * also waiting writers. >+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] >+ **/ >+ >+class WriterPreferenceReadWriteLock implements ReadWriteLock { >+ >+ protected long activeReaders_ = 0; >+ protected Thread activeWriter_ = null; >+ protected long waitingReaders_ = 0; >+ protected long waitingWriters_ = 0; >+ >+ >+ protected final ReaderLock readerLock_ = new ReaderLock(); >+ protected final WriterLock writerLock_ = new WriterLock(); >+ >+ public Sync writeLock() { return writerLock_; } >+ public Sync readLock() { return readerLock_; } >+ >+ /* >+ A bunch of small synchronized methods are needed >+ to allow communication from the Lock objects >+ back to this object, that serves as controller >+ */ >+ >+ >+ protected synchronized void cancelledWaitingReader() { --waitingReaders_; } >+ protected synchronized void cancelledWaitingWriter() { --waitingWriters_; } >+ >+ >+ /** Override this method to change to reader preference **/ >+ protected boolean allowReader() { >+ return activeWriter_ == null && waitingWriters_ == 0; >+ } >+ >+ >+ protected synchronized boolean startRead() { >+ boolean allowRead = allowReader(); >+ if (allowRead) ++activeReaders_; >+ return allowRead; >+ } >+ >+ protected synchronized boolean startWrite() { >+ >+ // The allowWrite expression cannot be modified without >+ // also changing startWrite, so is hard-wired >+ >+ boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0); >+ if (allowWrite) activeWriter_ = Thread.currentThread(); >+ return allowWrite; >+ } >+ >+ >+ /* >+ Each of these variants is needed to maintain atomicity >+ of wait counts during wait loops. They could be >+ made faster by manually inlining each other. We hope that >+ compilers do this for us though. >+ */ >+ >+ protected synchronized boolean startReadFromNewReader() { >+ boolean pass = startRead(); >+ if (!pass) ++waitingReaders_; >+ return pass; >+ } >+ >+ protected synchronized boolean startWriteFromNewWriter() { >+ boolean pass = startWrite(); >+ if (!pass) ++waitingWriters_; >+ return pass; >+ } >+ >+ protected synchronized boolean startReadFromWaitingReader() { >+ boolean pass = startRead(); >+ if (pass) --waitingReaders_; >+ return pass; >+ } >+ >+ protected synchronized boolean startWriteFromWaitingWriter() { >+ boolean pass = startWrite(); >+ if (pass) --waitingWriters_; >+ return pass; >+ } >+ >+ /** >+ * Called upon termination of a read. >+ * Returns the object to signal to wake up a waiter, or null if no such >+ **/ >+ protected synchronized Signaller endRead() { >+ if (--activeReaders_ == 0 && waitingWriters_ > 0) >+ return writerLock_; >+ else >+ return null; >+ } >+ >+ >+ /** >+ * Called upon termination of a write. >+ * Returns the object to signal to wake up a waiter, or null if no such >+ **/ >+ protected synchronized Signaller endWrite() { >+ activeWriter_ = null; >+ if (waitingReaders_ > 0 && allowReader()) >+ return readerLock_; >+ else if (waitingWriters_ > 0) >+ return writerLock_; >+ else >+ return null; >+ } >+ >+ >+ /** >+ * Reader and Writer requests are maintained in two different >+ * wait sets, by two different objects. These objects do not >+ * know whether the wait sets need notification since they >+ * don't know preference rules. So, each supports a >+ * method that can be selected by main controlling object >+ * to perform the notifications. This base class simplifies mechanics. >+ **/ >+ >+ protected abstract class Signaller { // base for ReaderLock and WriterLock >+ abstract void signalWaiters(); >+ } >+ >+ protected class ReaderLock extends Signaller implements Sync { >+ >+ public void acquire() throws InterruptedException { >+ if (Thread.interrupted()) throw new InterruptedException(); >+ InterruptedException ie = null; >+ synchronized(this) { >+ if (!startReadFromNewReader()) { >+ for (;;) { >+ try { >+ ReaderLock.this.wait(); >+ if (startReadFromWaitingReader()) >+ return; >+ } >+ catch(InterruptedException ex){ >+ cancelledWaitingReader(); >+ ie = ex; >+ break; >+ } >+ } >+ } >+ } >+ if (ie != null) { >+ // fall through outside synch on interrupt. >+ // This notification is not really needed here, >+ // but may be in plausible subclasses >+ writerLock_.signalWaiters(); >+ throw ie; >+ } >+ } >+ >+ >+ public void release() { >+ Signaller s = endRead(); >+ if (s != null) s.signalWaiters(); >+ } >+ >+ >+ synchronized void signalWaiters() { ReaderLock.this.notifyAll(); } >+ >+ public boolean attempt(long msecs) throws InterruptedException { >+ if (Thread.interrupted()) throw new InterruptedException(); >+ InterruptedException ie = null; >+ synchronized(this) { >+ if (msecs <= 0) >+ return startRead(); >+ else if (startReadFromNewReader()) >+ return true; >+ else { >+ long waitTime = msecs; >+ long start = System.currentTimeMillis(); >+ for (;;) { >+ try { ReaderLock.this.wait(waitTime); } >+ catch(InterruptedException ex){ >+ cancelledWaitingReader(); >+ ie = ex; >+ break; >+ } >+ if (startReadFromWaitingReader()) >+ return true; >+ else { >+ waitTime = msecs - (System.currentTimeMillis() - start); >+ if (waitTime <= 0) { >+ cancelledWaitingReader(); >+ break; >+ } >+ } >+ } >+ } >+ } >+ // safeguard on interrupt or timeout: >+ writerLock_.signalWaiters(); >+ if (ie != null) throw ie; >+ else return false; // timed out >+ } >+ >+ } >+ >+ protected class WriterLock extends Signaller implements Sync { >+ >+ public void acquire() throws InterruptedException { >+ if (Thread.interrupted()) throw new InterruptedException(); >+ InterruptedException ie = null; >+ synchronized(this) { >+ if (!startWriteFromNewWriter()) { >+ for (;;) { >+ try { >+ WriterLock.this.wait(); >+ if (startWriteFromWaitingWriter()) >+ return; >+ } >+ catch(InterruptedException ex){ >+ cancelledWaitingWriter(); >+ WriterLock.this.notify(); >+ ie = ex; >+ break; >+ } >+ } >+ } >+ } >+ if (ie != null) { >+ // Fall through outside synch on interrupt. >+ // On exception, we may need to signal readers. >+ // It is not worth checking here whether it is strictly necessary. >+ readerLock_.signalWaiters(); >+ throw ie; >+ } >+ } >+ >+ public void release(){ >+ Signaller s = endWrite(); >+ if (s != null) s.signalWaiters(); >+ } >+ >+ synchronized void signalWaiters() { WriterLock.this.notify(); } >+ >+ public boolean attempt(long msecs) throws InterruptedException { >+ if (Thread.interrupted()) throw new InterruptedException(); >+ InterruptedException ie = null; >+ synchronized(this) { >+ if (msecs <= 0) >+ return startWrite(); >+ else if (startWriteFromNewWriter()) >+ return true; >+ else { >+ long waitTime = msecs; >+ long start = System.currentTimeMillis(); >+ for (;;) { >+ try { WriterLock.this.wait(waitTime); } >+ catch(InterruptedException ex){ >+ cancelledWaitingWriter(); >+ WriterLock.this.notify(); >+ ie = ex; >+ break; >+ } >+ if (startWriteFromWaitingWriter()) >+ return true; >+ else { >+ waitTime = msecs - (System.currentTimeMillis() - start); >+ if (waitTime <= 0) { >+ cancelledWaitingWriter(); >+ WriterLock.this.notify(); >+ break; >+ } >+ } >+ } >+ } >+ } >+ >+ readerLock_.signalWaiters(); >+ if (ie != null) throw ie; >+ else return false; // timed out >+ } >+ >+ } >+ >+ >+ >+} >+ > >Property changes on: src/java/org/apache/log4j/concurrent/WriterPreferenceReadWriteLock.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: src/java/org/apache/log4j/concurrent/Sync.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/Sync.java (revision 0) >+++ src/java/org/apache/log4j/concurrent/Sync.java (revision 0) >@@ -0,0 +1,340 @@ >+/* >+ File: Sync.java >+ >+ Originally written by Doug Lea and released into the public domain. >+ This may be used for any purposes whatsoever without acknowledgment. >+ Thanks for the assistance and support of Sun Microsystems Labs, >+ and everyone contributing, testing, and using this code. >+ >+ History: >+ Date Who What >+ 11Jun1998 dl Create public version >+ 5Aug1998 dl Added some convenient time constants >+*/ >+ >+package org.apache.log4j.concurrent; >+ >+/** >+ * Main interface for locks, gates, and conditions. >+ * <p> >+ * Sync objects isolate waiting and notification for particular >+ * logical states, resource availability, events, and the like that are >+ * shared across multiple threads. Use of Syncs sometimes >+ * (but by no means always) adds flexibility and efficiency >+ * compared to the use of plain java monitor methods >+ * and locking, and are sometimes (but by no means always) >+ * simpler to program with. >+ * <p> >+ * >+ * Most Syncs are intended to be used primarily (although >+ * not exclusively) in before/after constructions such as: >+ * <pre> >+ * class X { >+ * Sync gate; >+ * // ... >+ * >+ * public void m() { >+ * try { >+ * gate.acquire(); // block until condition holds >+ * try { >+ * // ... method body >+ * } >+ * finally { >+ * gate.release() >+ * } >+ * } >+ * catch (InterruptedException ex) { >+ * // ... evasive action >+ * } >+ * } >+ * >+ * public void m2(Sync cond) { // use supplied condition >+ * try { >+ * if (cond.attempt(10)) { // try the condition for 10 ms >+ * try { >+ * // ... method body >+ * } >+ * finally { >+ * cond.release() >+ * } >+ * } >+ * } >+ * catch (InterruptedException ex) { >+ * // ... evasive action >+ * } >+ * } >+ * } >+ * </pre> >+ * Syncs may be used in somewhat tedious but more flexible replacements >+ * for built-in Java synchronized blocks. For example: >+ * <pre> >+ * class HandSynched { >+ * private double state_ = 0.0; >+ * private final Sync lock; // use lock type supplied in constructor >+ * public HandSynched(Sync l) { lock = l; } >+ * >+ * public void changeState(double d) { >+ * try { >+ * lock.acquire(); >+ * try { state_ = updateFunction(d); } >+ * finally { lock.release(); } >+ * } >+ * catch(InterruptedException ex) { } >+ * } >+ * >+ * public double getState() { >+ * double d = 0.0; >+ * try { >+ * lock.acquire(); >+ * try { d = accessFunction(state_); } >+ * finally { lock.release(); } >+ * } >+ * catch(InterruptedException ex){} >+ * return d; >+ * } >+ * private double updateFunction(double d) { ... } >+ * private double accessFunction(double d) { ... } >+ * } >+ * </pre> >+ * If you have a lot of such methods, and they take a common >+ * form, you can standardize this using wrappers. Some of these >+ * wrappers are standardized in LockedExecutor, but you can make others. >+ * For example: >+ * <pre> >+ * class HandSynchedV2 { >+ * private double state_ = 0.0; >+ * private final Sync lock; // use lock type supplied in constructor >+ * public HandSynchedV2(Sync l) { lock = l; } >+ * >+ * protected void runSafely(Runnable r) { >+ * try { >+ * lock.acquire(); >+ * try { r.run(); } >+ * finally { lock.release(); } >+ * } >+ * catch (InterruptedException ex) { // propagate without throwing >+ * Thread.currentThread().interrupt(); >+ * } >+ * } >+ * >+ * public void changeState(double d) { >+ * runSafely(new Runnable() { >+ * public void run() { state_ = updateFunction(d); } >+ * }); >+ * } >+ * // ... >+ * } >+ * </pre> >+ * <p> >+ * One reason to bother with such constructions is to use deadlock- >+ * avoiding back-offs when dealing with locks involving multiple objects. >+ * For example, here is a Cell class that uses attempt to back-off >+ * and retry if two Cells are trying to swap values with each other >+ * at the same time. >+ * <pre> >+ * class Cell { >+ * long value; >+ * Sync lock = ... // some sync implementation class >+ * void swapValue(Cell other) { >+ * for (;;) { >+ * try { >+ * lock.acquire(); >+ * try { >+ * if (other.lock.attempt(100)) { >+ * try { >+ * long t = value; >+ * value = other.value; >+ * other.value = t; >+ * return; >+ * } >+ * finally { other.lock.release(); } >+ * } >+ * } >+ * finally { lock.release(); } >+ * } >+ * catch (InterruptedException ex) { return; } >+ * } >+ * } >+ * } >+ *</pre> >+ * <p> >+ * Here is an even fancier version, that uses lock re-ordering >+ * upon conflict: >+ * <pre> >+ * class Cell { >+ * long value; >+ * Sync lock = ...; >+ * private static boolean trySwap(Cell a, Cell b) { >+ * a.lock.acquire(); >+ * try { >+ * if (!b.lock.attempt(0)) >+ * return false; >+ * try { >+ * long t = a.value; >+ * a.value = b.value; >+ * b.value = t; >+ * return true; >+ * } >+ * finally { other.lock.release(); } >+ * } >+ * finally { lock.release(); } >+ * return false; >+ * } >+ * >+ * void swapValue(Cell other) { >+ * try { >+ * while (!trySwap(this, other) && >+ * !tryswap(other, this)) >+ * Thread.sleep(1); >+ * } >+ * catch (InterruptedException ex) { return; } >+ * } >+ *} >+ *</pre> >+ * <p> >+ * Interruptions are in general handled as early as possible. >+ * Normally, InterruptionExceptions are thrown >+ * in acquire and attempt(msec) if interruption >+ * is detected upon entry to the method, as well as in any >+ * later context surrounding waits. >+ * However, interruption status is ignored in release(); >+ * <p> >+ * Timed versions of attempt report failure via return value. >+ * If so desired, you can transform such constructions to use exception >+ * throws via >+ * <pre> >+ * if (!c.attempt(timeval)) throw new TimeoutException(timeval); >+ * </pre> >+ * <p> >+ * The TimoutSync wrapper class can be used to automate such usages. >+ * <p> >+ * All time values are expressed in milliseconds as longs, which have a maximum >+ * value of Long.MAX_VALUE, or almost 300,000 centuries. It is not >+ * known whether JVMs actually deal correctly with such extreme values. >+ * For convenience, some useful time values are defined as static constants. >+ * <p> >+ * All implementations of the three Sync methods guarantee to >+ * somehow employ Java <code>synchronized</code> methods or blocks, >+ * and so entail the memory operations described in JLS >+ * chapter 17 which ensure that variables are loaded and flushed >+ * within before/after constructions. >+ * <p> >+ * Syncs may also be used in spinlock constructions. Although >+ * it is normally best to just use acquire(), various forms >+ * of busy waits can be implemented. For a simple example >+ * (but one that would probably never be preferable to using acquire()): >+ * <pre> >+ * class X { >+ * Sync lock = ... >+ * void spinUntilAcquired() throws InterruptedException { >+ * // Two phase. >+ * // First spin without pausing. >+ * int purespins = 10; >+ * for (int i = 0; i < purespins; ++i) { >+ * if (lock.attempt(0)) >+ * return true; >+ * } >+ * // Second phase - use timed waits >+ * long waitTime = 1; // 1 millisecond >+ * for (;;) { >+ * if (lock.attempt(waitTime)) >+ * return true; >+ * else >+ * waitTime = waitTime * 3 / 2 + 1; // increase 50% >+ * } >+ * } >+ * } >+ * </pre> >+ * <p> >+ * In addition pure synchronization control, Syncs >+ * may be useful in any context requiring before/after methods. >+ * For example, you can use an ObservableSync >+ * (perhaps as part of a LayeredSync) in order to obtain callbacks >+ * before and after each method invocation for a given class. >+ * <p> >+ >+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] >+**/ >+ >+ >+public interface Sync { >+ >+ /** >+ * Wait (possibly forever) until successful passage. >+ * Fail only upon interuption. Interruptions always result in >+ * `clean' failures. On failure, you can be sure that it has not >+ * been acquired, and that no >+ * corresponding release should be performed. Conversely, >+ * a normal return guarantees that the acquire was successful. >+ **/ >+ >+ public void acquire() throws InterruptedException; >+ >+ /** >+ * Wait at most msecs to pass; report whether passed. >+ * <p> >+ * The method has best-effort semantics: >+ * The msecs bound cannot >+ * be guaranteed to be a precise upper bound on wait time in Java. >+ * Implementations generally can only attempt to return as soon as possible >+ * after the specified bound. Also, timers in Java do not stop during garbage >+ * collection, so timeouts can occur just because a GC intervened. >+ * So, msecs arguments should be used in >+ * a coarse-grained manner. Further, >+ * implementations cannot always guarantee that this method >+ * will return at all without blocking indefinitely when used in >+ * unintended ways. For example, deadlocks may be encountered >+ * when called in an unintended context. >+ * <p> >+ * @param msecs the number of milleseconds to wait. >+ * An argument less than or equal to zero means not to wait at all. >+ * However, this may still require >+ * access to a synchronization lock, which can impose unbounded >+ * delay if there is a lot of contention among threads. >+ * @return true if acquired >+ **/ >+ >+ public boolean attempt(long msecs) throws InterruptedException; >+ >+ /** >+ * Potentially enable others to pass. >+ * <p> >+ * Because release does not raise exceptions, >+ * it can be used in `finally' clauses without requiring extra >+ * embedded try/catch blocks. But keep in mind that >+ * as with any java method, implementations may >+ * still throw unchecked exceptions such as Error or NullPointerException >+ * when faced with uncontinuable errors. However, these should normally >+ * only be caught by higher-level error handlers. >+ **/ >+ >+ public void release(); >+ >+ /** One second, in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_SECOND = 1000; >+ >+ /** One minute, in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_MINUTE = 60 * ONE_SECOND; >+ >+ /** One hour, in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_HOUR = 60 * ONE_MINUTE; >+ >+ /** One day, in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_DAY = 24 * ONE_HOUR; >+ >+ /** One week, in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_WEEK = 7 * ONE_DAY; >+ >+ /** One year in milliseconds; convenient as a time-out value **/ >+ // Not that it matters, but there is some variation across >+ // standard sources about value at msec precision. >+ // The value used is the same as in java.util.GregorianCalendar >+ public static final long ONE_YEAR = (long)(365.2425 * ONE_DAY); >+ >+ /** One century in milliseconds; convenient as a time-out value **/ >+ public static final long ONE_CENTURY = 100 * ONE_YEAR; >+ >+ >+} >+ >+ > >Property changes on: src/java/org/apache/log4j/concurrent/Sync.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: src/java/org/apache/log4j/concurrent/FileAppender.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/FileAppender.java (revision 463475) >+++ src/java/org/apache/log4j/concurrent/FileAppender.java (working copy) >@@ -17,11 +17,13 @@ > package org.apache.log4j.concurrent; > > import java.io.Writer; >+import java.io.OutputStreamWriter; > import java.io.BufferedWriter; > import java.io.File; > import java.io.FileOutputStream; > import java.io.FileNotFoundException; > import java.io.IOException; >+import java.io.OutputStream; > > import org.apache.log4j.Layout; > import org.apache.log4j.helpers.OptionConverter; >@@ -251,40 +253,44 @@ > public void setFile( > String filename, boolean append, boolean bufferedIO, int bufferSize) > throws IOException { >- getLogger().debug("setFile called: {}, {}", fileName, append?"true":"false"); >+ getLogger().debug("setFile called: {}, {}", filename, Boolean.valueOf(append)); > >- FileOutputStream ostream; >+ this.fileAppend = append; >+ this.bufferedIO = bufferedIO; >+ this.fileName = filename; >+ this.bufferSize = bufferSize; >+ >+ FileOutputStream ostream = createOutputStream(); >+ Writer writer = createWriter(ostream); >+ if (bufferedIO) { >+ writer = new BufferedWriter(writer, bufferSize); >+ } >+ >+ setWriter(writer); >+ getLogger().debug("setFile ended"); >+ } >+ >+ protected FileOutputStream createOutputStream() >+ throws IOException >+ { > try { > // > // attempt to create file > // >- ostream = new FileOutputStream(filename, append); >+ return new FileOutputStream(fileName, fileAppend); > } catch(FileNotFoundException ex) { > // > // if parent directory does not exist then > // attempt to create it and try to create file > // see bug 9150 > // >- File parentDir = new File(new File(filename).getParent()); >- if(!parentDir.exists() && parentDir.mkdirs()) { >- ostream = new FileOutputStream(filename, append); >+ File parentDir = new File(new File(fileName).getParent()); >+ if (!parentDir.exists() && parentDir.mkdirs()) { >+ return new FileOutputStream(fileName, fileAppend); > } else { > throw ex; > } > } >- Writer writer = createWriter(ostream); >- >- if (bufferedIO) { >- writer = new BufferedWriter(writer, bufferSize); >- } >- >- this.fileAppend = append; >- this.bufferedIO = bufferedIO; >- this.fileName = filename; >- this.bufferSize = bufferSize; >- >- setWriter(writer); >- getLogger().debug("setFile ended"); > } > > } >Index: src/java/org/apache/log4j/concurrent/ReentrantWriterPreferenceReadWriteLock.java >=================================================================== >--- src/java/org/apache/log4j/concurrent/ReentrantWriterPreferenceReadWriteLock.java (revision 0) >+++ src/java/org/apache/log4j/concurrent/ReentrantWriterPreferenceReadWriteLock.java (revision 0) >@@ -0,0 +1,160 @@ >+/* >+ File: ReentrantWriterPreferenceReadWriteLock.java >+ >+ Originally written by Doug Lea and released into the public domain. >+ This may be used for any purposes whatsoever without acknowledgment. >+ Thanks for the assistance and support of Sun Microsystems Labs, >+ and everyone contributing, testing, and using this code. >+ >+ History: >+ Date Who What >+ 26aug1998 dl Create public version >+ 7sep2000 dl Readers are now also reentrant >+ 19jan2001 dl Allow read->write upgrades if the only reader >+ 10dec2002 dl Throw IllegalStateException on extra release >+*/ >+ >+package org.apache.log4j.concurrent; >+import java.util.*; >+ >+/** >+ * A writer-preference ReadWriteLock that allows both readers and >+ * writers to reacquire >+ * read or write locks in the style of a ReentrantLock. >+ * Readers are not allowed until all write locks held by >+ * the writing thread have been released. >+ * Among other applications, reentrancy can be useful when >+ * write locks are held during calls or callbacks to methods that perform >+ * reads under read locks. >+ * <p> >+ * <b>Sample usage</b>. Here is a code sketch showing how to exploit >+ * reentrancy to perform lock downgrading after updating a cache: >+ * <pre> >+ * class CachedData { >+ * Object data; >+ * volatile boolean cacheValid; >+ * ReentrantWriterPreferenceReadWriteLock rwl = ... >+ * >+ * void processCachedData() { >+ * rwl.readLock().acquire(); >+ * if (!cacheValid) { >+ * >+ * // upgrade lock: >+ * rwl.readLock().release(); // must release first to obtain writelock >+ * rwl.writeLock().acquire(); >+ * if (!cacheValid) { // recheck >+ * data = ... >+ * cacheValid = true; >+ * } >+ * // downgrade lock >+ * rwl.readLock().acquire(); // reacquire read without giving up lock >+ * rwl.writeLock().release(); // release write, still hold read >+ * } >+ * >+ * use(data); >+ * rwl.readLock().release(); >+ * } >+ * } >+ * </pre> >+ * >+ * >+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] >+ * @see ReentrantLock >+ **/ >+ >+class ReentrantWriterPreferenceReadWriteLock extends WriterPreferenceReadWriteLock { >+ >+ /** Number of acquires on write lock by activeWriter_ thread **/ >+ protected long writeHolds_ = 0; >+ >+ /** Number of acquires on read lock by any reader thread **/ >+ protected HashMap readers_ = new HashMap(); >+ >+ /** cache/reuse the special Integer value one to speed up readlocks **/ >+ protected static final Integer IONE = new Integer(1); >+ >+ >+ protected boolean allowReader() { >+ return (activeWriter_ == null && waitingWriters_ == 0) || >+ activeWriter_ == Thread.currentThread(); >+ } >+ >+ protected synchronized boolean startRead() { >+ Thread t = Thread.currentThread(); >+ Object c = readers_.get(t); >+ if (c != null) { // already held -- just increment hold count >+ readers_.put(t, new Integer(((Integer)(c)).intValue()+1)); >+ ++activeReaders_; >+ return true; >+ } >+ else if (allowReader()) { >+ readers_.put(t, IONE); >+ ++activeReaders_; >+ return true; >+ } >+ else >+ return false; >+ } >+ >+ protected synchronized boolean startWrite() { >+ if (activeWriter_ == Thread.currentThread()) { // already held; re-acquire >+ ++writeHolds_; >+ return true; >+ } >+ else if (writeHolds_ == 0) { >+ if (activeReaders_ == 0 || >+ (readers_.size() == 1 && >+ readers_.get(Thread.currentThread()) != null)) { >+ activeWriter_ = Thread.currentThread(); >+ writeHolds_ = 1; >+ return true; >+ } >+ else >+ return false; >+ } >+ else >+ return false; >+ } >+ >+ >+ protected synchronized Signaller endRead() { >+ Thread t = Thread.currentThread(); >+ Object c = readers_.get(t); >+ if (c == null) >+ throw new IllegalStateException(); >+ --activeReaders_; >+ if (c != IONE) { // more than one hold; decrement count >+ int h = ((Integer)(c)).intValue()-1; >+ Integer ih = (h == 1)? IONE : new Integer(h); >+ readers_.put(t, ih); >+ return null; >+ } >+ else { >+ readers_.remove(t); >+ >+ if (writeHolds_ > 0) // a write lock is still held by current thread >+ return null; >+ else if (activeReaders_ == 0 && waitingWriters_ > 0) >+ return writerLock_; >+ else >+ return null; >+ } >+ } >+ >+ protected synchronized Signaller endWrite() { >+ --writeHolds_; >+ if (writeHolds_ > 0) // still being held >+ return null; >+ else { >+ activeWriter_ = null; >+ if (waitingReaders_ > 0 && allowReader()) >+ return readerLock_; >+ else if (waitingWriters_ > 0) >+ return writerLock_; >+ else >+ return null; >+ } >+ } >+ >+} >+ > >Property changes on: src/java/org/apache/log4j/concurrent/ReentrantWriterPreferenceReadWriteLock.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: src/java/org/apache/log4j/rolling/RollingFileAppender.java >=================================================================== >--- src/java/org/apache/log4j/rolling/RollingFileAppender.java (revision 463475) >+++ src/java/org/apache/log4j/rolling/RollingFileAppender.java (working copy) >@@ -198,7 +198,7 @@ > if (rollingPolicy != null) { > Exception exception = null; > >- synchronized (this) { >+ synchronized (this) { > // > // if a previous async task is still running > //} >@@ -414,7 +414,7 @@ > * Wrapper for OutputStream that will report all write > * operations back to this class for file length calculations. > */ >- private static class CountingOutputStream extends OutputStream { >+ static class CountingOutputStream extends OutputStream { > /** > * Wrapped output stream. > */ >Index: tests\src\java\org\apache\log4j\concurrent\FileAppenderTest.java >=================================================================== >--- tests\src\java\org\apache\log4j\concurrent\FileAppenderTest.java (revision 0) >+++ tests\src\java\org\apache\log4j\concurrent\FileAppenderTest.java (revision 0) >@@ -0,0 +1,87 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import java.io.File; >+import java.io.FileReader; >+import java.io.BufferedReader; >+import junit.framework.TestCase; >+import org.apache.log4j.Layout; >+import org.apache.log4j.Level; >+import org.apache.log4j.Logger; >+import org.apache.log4j.SimpleLayout; >+ >+/** >+ * Tests of FileAppender. >+ */ >+public class FileAppenderTest extends TestCase { >+ >+ private Logger log = Logger.getLogger(FileAppender.class); >+ private FileAppender appender = new FileAppender(); >+ private SimpleLayout layout = new SimpleLayout(); >+ private File f1; >+ >+ protected void setUp() throws Exception { >+ f1 = File.createTempFile("FileAppenderTest", ".tmp"); >+ f1.deleteOnExit(); >+ appender.setLayout(layout); >+ appender.activateOptions(); // won't work >+ appender.setFile(f1.toString()); >+ log.addAppender(appender); >+ } >+ >+ /** >+ * Tests FileAppender methods and writing. >+ */ >+ public void testBasic() throws Exception { >+ assertEquals(false, appender.isActive()); >+ assertEquals(true, appender.getAppend()); >+ assertEquals(f1.toString(), appender.getFile()); >+ >+ // Check and change default options >+ assertEquals(true, appender.getBufferedIO()); >+ appender.setBufferedIO(false); >+ assertEquals(false, appender.getBufferedIO()); >+ assertEquals(true, appender.getAppend()); >+ appender.setAppend(false); >+ assertEquals(false, appender.getAppend()); >+ appender.setBufferSize(400); >+ assertEquals(400, appender.getBufferSize()); >+ appender.activateOptions(); // works >+ >+ log.debug("HI"); >+ appender.close(); >+ BufferedReader r = new BufferedReader(new FileReader(f1)); >+ assertEquals("DEBUG - HI", r.readLine()); >+ } >+ >+ /** >+ * Tests that FileAppender works with default options. >+ */ >+ public void testDefaultOptions() throws Exception { >+ appender.activateOptions(); >+ log.debug("HI"); >+ appender.close(); >+ BufferedReader r = new BufferedReader(new FileReader(f1)); >+ assertEquals("DEBUG - HI", r.readLine()); >+ } >+ >+} >+ >+ >+ >+ >Index: tests/src/java/org/apache/log4j/concurrent/SynchronizedBooleanTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/SynchronizedBooleanTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/SynchronizedBooleanTest.java (revision 0) >@@ -0,0 +1,39 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import junit.framework.TestCase; >+ >+ >+/** >+ * Tests of SynchronizedBoolean. >+ */ >+public class SynchronizedBooleanTest extends TestCase { >+ >+ /** >+ * Tests SynchronizedBoolean get set and toString. >+ */ >+ public void testBasic() { >+ SynchronizedBoolean sb = new SynchronizedBoolean(true); >+ assertEquals(true, sb.get()); >+ sb.set(false); >+ assertEquals(false, sb.get()); >+ assertEquals("false", sb.toString()); >+ } >+ >+} >+ > >Property changes on: tests/src/java/org/apache/log4j/concurrent/SynchronizedBooleanTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/src/java/org/apache/log4j/concurrent/ConcurrentAppenderTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/ConcurrentAppenderTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/ConcurrentAppenderTest.java (revision 0) >@@ -0,0 +1,145 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import junit.framework.TestCase; >+import org.apache.log4j.Logger; >+import org.apache.log4j.Level; >+import org.apache.log4j.spi.LoggingEvent; >+import org.apache.log4j.SimpleLayout; >+import org.apache.log4j.filter.DenyAllFilter; >+import org.apache.log4j.filter.StringMatchFilter; >+ >+/** >+ * Tests of ConcurrentAppender. >+ */ >+public class ConcurrentAppenderTest extends TestCase { >+ >+ private Logger log = Logger.getLogger(ConcurrentAppenderTest.class); >+ private MyConcurrentAppender a = new MyConcurrentAppender(); >+ private String name = "name"; >+ private String msg = "Hello, World"; >+ private SimpleLayout layout = new SimpleLayout(); >+ private DenyAllFilter denyFilter = new DenyAllFilter(); >+ private StringMatchFilter stringMatchFilter = new StringMatchFilter(); >+ { >+ stringMatchFilter.setStringToMatch("yo"); >+ } >+ >+ /** >+ * Tests set and get methods. >+ */ >+ public void testSetGet() { >+ assertEquals(false, a.isActive()); >+ assertEquals(false, a.isClosed()); >+ assertEquals(false, a.getClosed()); >+ assertEquals(null, a.getName()); >+ a.setName(name); >+ assertEquals(name, a.getName()); >+ assertEquals(null, a.getThreshold()); >+ a.setThreshold(Level.INFO); >+ assertEquals(Level.INFO, a.getThreshold()); >+ assertEquals(null, a.getLayout()); >+ a.setLayout(layout); >+ assertEquals(layout, a.getLayout()); >+ >+ assertNotNull(a.getErrorHandler()); >+ a.setErrorHandler(null); >+ assertNotNull(a.toString()); >+ } >+ >+ /** >+ * Tests log methods, threshold, filter. >+ */ >+ public void testLog() { >+ log.addAppender(a); >+ log.debug(msg); >+ assertEquals("not activated", null, a.event); >+ a.activateOptions(); >+ log.debug(msg); >+ assertNotNull(a.event); >+ >+ a.setThreshold(Level.INFO); >+ a.event = null; >+ log.debug(msg); >+ assertEquals("filtered", null, a.event); >+ log.fatal(msg); >+ assertNotNull(a.event); >+ >+ a.event = null; >+ stringMatchFilter.setStringToMatch("yo"); >+ a.addFilter(stringMatchFilter); >+ a.addFilter(denyFilter); >+ log.fatal("Not y and o"); >+ assertEquals("filtered", null, a.event); >+ log.fatal("yo yo yo"); >+ assertNotNull(a.event); >+ assertEquals(stringMatchFilter, a.getFilter()); >+ >+ a.clearFilters(); >+ a.event = null; >+ log.fatal("Not y and o"); >+ assertNotNull("no longer filtered", a.event); >+ } >+ >+ /** >+ * Tests active and close methods. >+ */ >+ public void testClose() { >+ log.addAppender(a); >+ log.debug("not active"); >+ a.activateOptions(); >+ assertEquals(true, a.isActive()); >+ a.close(); >+ assertEquals(true, a.internalClosed); >+ assertEquals(true, a.isClosed()); >+ assertEquals(true, a.getClosed()); >+ log.debug("not logged"); >+ assertEquals("closed", null, a.event); >+ a.close(); // shouldn't call internalClose() twice >+ assertEquals(true, a.internalClosed); >+ assertEquals(true, a.isClosed()); >+ } >+ >+ class MyConcurrentAppender extends ConcurrentAppender { >+ >+ LoggingEvent event; >+ boolean internalClosed; >+ >+ MyConcurrentAppender() { >+ super(false); >+ } >+ >+ protected void append(LoggingEvent event) { >+ this.event = event; >+ } >+ >+ public boolean requiresLayout() { >+ return true; >+ } >+ >+ protected void internalClose() { >+ if (internalClosed) >+ throw new IllegalStateException(); >+ internalClosed = true; >+ } >+ >+ } >+ >+} >+ >+ > >Property changes on: tests/src/java/org/apache/log4j/concurrent/ConcurrentAppenderTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/src/java/org/apache/log4j/concurrent/ConsoleAppenderTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/ConsoleAppenderTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/ConsoleAppenderTest.java (revision 0) >@@ -0,0 +1,74 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import java.io.PrintStream; >+import java.io.ByteArrayOutputStream; >+import junit.framework.TestCase; >+import org.apache.log4j.Layout; >+import org.apache.log4j.Level; >+import org.apache.log4j.Logger; >+import org.apache.log4j.SimpleLayout; >+ >+/** >+ * Tests of ConsoleAppender. >+ */ >+public class ConsoleAppenderTest extends TestCase { >+ >+ private Logger log = Logger.getLogger(ConsoleAppender.class); >+ private ConsoleAppender appender = new ConsoleAppender(); >+ private SimpleLayout layout = new SimpleLayout(); >+ >+ private ByteArrayOutputStream bo1 = new ByteArrayOutputStream(); >+ private ByteArrayOutputStream bo2 = new ByteArrayOutputStream(); >+ private PrintStream err = new PrintStream(bo1); >+ private PrintStream out = new PrintStream(bo2); >+ { >+ appender.setLayout(layout); >+ log.addAppender(appender); >+ } >+ >+ /** >+ * Tests ConsoleAppender get set and toString. >+ */ >+ public void testBasic() { >+ assertEquals(false, appender.isActive()); >+ System.setErr(err); >+ appender.setTarget("System.err"); >+ assertEquals(false, appender.isActive()); >+ appender.activateOptions(); >+ assertEquals(true, appender.isActive()); >+ log.debug("HI"); >+ assertEquals("DEBUG - HI", bo1.toString().trim()); >+ assertEquals("", bo2.toString()); >+ >+ appender.setTarget("System.out"); >+ appender.activateOptions(); >+ System.setOut(out); >+ log.debug("HI"); >+ assertEquals("not following", "", bo2.toString().trim()); >+ >+ appender.setFollow(true); >+ appender.activateOptions(); >+ log.debug("HI"); >+ assertEquals("DEBUG - HI", bo2.toString().trim()); >+ } >+ >+} >+ >+ >+ > >Property changes on: tests/src/java/org/apache/log4j/concurrent/ConsoleAppenderTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/src/java/org/apache/log4j/concurrent/RollingFileAppenderTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/RollingFileAppenderTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/RollingFileAppenderTest.java (revision 0) >@@ -0,0 +1,98 @@ >+/* >+ * Copyright 1999,2005 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import java.io.File; >+import java.io.FileOutputStream; >+ >+import junit.framework.TestCase; >+ >+import org.apache.log4j.Appender; >+import org.apache.log4j.LogManager; >+import org.apache.log4j.Logger; >+import org.apache.log4j.PatternLayout; >+import org.apache.log4j.util.Compare; >+import org.apache.log4j.rolling.SizeBasedTriggeringPolicy; >+import org.apache.log4j.rolling.FixedWindowRollingPolicy; >+ >+ >+/** >+ * Tests RollingFileAppender in the concurrent library. >+ * Taken from SizeBasedRollingTest. >+ * Not a complete functionality test. >+ */ >+public class RollingFileAppenderTest extends TestCase { >+ Logger logger = Logger.getLogger(RollingFileAppenderTest.class); >+ >+ public RollingFileAppenderTest(String name) { >+ super(name); >+ } >+ >+ public void setUp() { >+ } >+ >+ public void tearDown() { >+ LogManager.shutdown(); >+ } >+ >+ /** >+ * Test basic rolling functionality with explicit setting of FileAppender.file. >+ */ >+ public void test2() throws Exception { >+ PatternLayout layout = new PatternLayout("%m\n"); >+ RollingFileAppender rfa = new RollingFileAppender(); >+ rfa.setName("ROLLING"); >+ rfa.setAppend(false); >+ rfa.setLayout(layout); >+ rfa.setFile("output/sizeBased-test2.log"); >+ >+ FixedWindowRollingPolicy swrp = new FixedWindowRollingPolicy(); >+ SizeBasedTriggeringPolicy sbtp = new SizeBasedTriggeringPolicy(); >+ >+ sbtp.setMaxFileSize(100); >+ swrp.setMinIndex(0); >+ >+ swrp.setFileNamePattern("output/sizeBased-test2.%i"); >+ swrp.activateOptions(); >+ >+ rfa.setRollingPolicy(swrp); >+ rfa.setTriggeringPolicy(sbtp); >+ rfa.activateOptions(); >+ logger.addAppender(rfa); >+ >+ // Write exactly 10 bytes with each log >+ for (int i = 0; i < 25; i++) { >+ if (i < 10) { >+ logger.debug("Hello---" + i); >+ } else if (i < 100) { >+ logger.debug("Hello--" + i); >+ } >+ } >+ >+ assertTrue(new File("output/sizeBased-test2.log").exists()); >+ assertTrue(new File("output/sizeBased-test2.0").exists()); >+ assertTrue(new File("output/sizeBased-test2.1").exists()); >+ >+ assertTrue(Compare.compare("output/sizeBased-test2.log", >+ "witness/rolling/sbr-test2.log")); >+ assertTrue(Compare.compare("output/sizeBased-test2.0", >+ "witness/rolling/sbr-test2.0")); >+ assertTrue(Compare.compare("output/sizeBased-test2.1", >+ "witness/rolling/sbr-test2.1")); >+ } >+ >+} > >Property changes on: tests/src/java/org/apache/log4j/concurrent/RollingFileAppenderTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/src/java/org/apache/log4j/concurrent/WriterAppenderTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/WriterAppenderTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/WriterAppenderTest.java (revision 0) >@@ -0,0 +1,133 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import java.io.Writer; >+import java.io.IOException; >+import junit.framework.TestCase; >+import org.apache.log4j.Layout; >+import org.apache.log4j.Level; >+import org.apache.log4j.Logger; >+import org.apache.log4j.SimpleLayout; >+ >+/** >+ * Tests of WriterAppender. >+ */ >+public class WriterAppenderTest extends TestCase { >+ >+ private Logger log = Logger.getLogger(ConcurrentAppenderTest.class); >+ private WriterAppender appender = new WriterAppender(); >+ private SimpleLayout layout = new SimpleLayout(); >+ >+ { >+ log.addAppender(appender); >+ layout.setFooter("F"); >+ layout.setHeader("H"); >+ } >+ >+ private MyStringWriter sw = new MyStringWriter(); >+ >+ private static class MyStringWriter extends Writer { >+ public boolean closed = false; >+ public boolean flushed = false; >+ public boolean toss = false; >+ StringBuffer sb = new StringBuffer(); >+ >+ public void write(char[] cbuf, int off, int len) throws IOException { >+ sb.append(cbuf, off, len); >+ if (toss) >+ throw new IOException(); >+ } >+ >+ public void flush() { >+ flushed = true; >+ } >+ >+ public void close() throws IOException { >+ closed = true; >+ } >+ >+ public String toString() { >+ return sb.toString(); >+ } >+ >+ } >+ >+ /** >+ * Tests WriterAppender get set and toString. >+ */ >+ public void testBasic() { >+ assertEquals(true, appender.getImmediateFlush()); >+ appender.setImmediateFlush(false); >+ assertEquals(false, appender.getImmediateFlush()); >+ appender.activateOptions(); >+ assertEquals(false, appender.isActive()); >+ appender.setLayout(layout); >+ appender.activateOptions(); >+ assertEquals(false, appender.isActive()); >+ appender.setWriter(sw); >+ appender.activateOptions(); >+ assertEquals(true, appender.isActive()); >+ appender.close(); >+ assertEquals(true, sw.closed); >+ assertEquals(true, sw.flushed); >+ assertEquals(true, appender.requiresLayout()); >+ appender.setEncoding("ASCII"); >+ assertEquals("ASCII", appender.getEncoding()); >+ } >+ >+ /** >+ * Tests WriterAppender output. >+ */ >+ public void testOutput() { >+ appender.setLayout(layout); >+ appender.setWriter(sw); >+ appender.activateOptions(); >+ log.debug("HI"); >+ assertEquals(true, sw.flushed); >+ assertEquals("HDEBUG - HI", sw.toString().trim()); >+ appender.close(); >+ log.debug("HI"); >+ assertEquals("HDEBUG - HI" + Layout.LINE_SEP + "F", sw.toString().trim()); >+ } >+ >+ /** >+ * Tests Throwable output. >+ */ >+ public void testThrowable() { >+ appender.setLayout(layout); >+ appender.setWriter(sw); >+ appender.activateOptions(); >+ appender.setImmediateFlush(false); >+ sw.flushed = false; >+ log.debug("HI", new Throwable()); >+ assertEquals(false, sw.flushed); >+ String s = sw.toString(); >+ assertTrue(":" + s, s.startsWith("HDEBUG - HI")); >+ assertTrue("has a stack trace", s.length() > 40); >+ >+ appender.setImmediateFlush(true); >+ assertEquals(true, appender.isActive()); >+ sw.toss = true; >+ log.debug("HI"); >+ log.debug("HI"); >+ assertEquals(false, appender.isActive()); >+ } >+ >+} >+ >+ > >Property changes on: tests/src/java/org/apache/log4j/concurrent/WriterAppenderTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/src/java/org/apache/log4j/concurrent/FileAppenderTest.java >=================================================================== >--- tests/src/java/org/apache/log4j/concurrent/FileAppenderTest.java (revision 0) >+++ tests/src/java/org/apache/log4j/concurrent/FileAppenderTest.java (revision 0) >@@ -0,0 +1,87 @@ >+/* >+ * Copyright 2006 The Apache Software Foundation. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); >+ * you may not use this file except in compliance with the License. >+ * You may obtain a copy of the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, >+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. >+ * See the License for the specific language governing permissions and >+ * limitations under the License. >+ */ >+ >+package org.apache.log4j.concurrent; >+ >+import java.io.File; >+import java.io.FileReader; >+import java.io.BufferedReader; >+import junit.framework.TestCase; >+import org.apache.log4j.Layout; >+import org.apache.log4j.Level; >+import org.apache.log4j.Logger; >+import org.apache.log4j.SimpleLayout; >+ >+/** >+ * Tests of FileAppender. >+ */ >+public class FileAppenderTest extends TestCase { >+ >+ private Logger log = Logger.getLogger(FileAppender.class); >+ private FileAppender appender = new FileAppender(); >+ private SimpleLayout layout = new SimpleLayout(); >+ private File f1; >+ >+ protected void setUp() throws Exception { >+ f1 = File.createTempFile("FileAppenderTest", ".tmp"); >+ f1.deleteOnExit(); >+ appender.setLayout(layout); >+ appender.activateOptions(); // won't work >+ appender.setFile(f1.toString()); >+ log.addAppender(appender); >+ } >+ >+ /** >+ * Tests FileAppender methods and writing. >+ */ >+ public void testBasic() throws Exception { >+ assertEquals(false, appender.isActive()); >+ assertEquals(true, appender.getAppend()); >+ assertEquals(f1.toString(), appender.getFile()); >+ >+ // Check and change default options >+ assertEquals(true, appender.getBufferedIO()); >+ appender.setBufferedIO(false); >+ assertEquals(false, appender.getBufferedIO()); >+ assertEquals(true, appender.getAppend()); >+ appender.setAppend(false); >+ assertEquals(false, appender.getAppend()); >+ appender.setBufferSize(400); >+ assertEquals(400, appender.getBufferSize()); >+ appender.activateOptions(); // works >+ >+ log.debug("HI"); >+ appender.close(); >+ BufferedReader r = new BufferedReader(new FileReader(f1)); >+ assertEquals("DEBUG - HI", r.readLine()); >+ } >+ >+ /** >+ * Tests that FileAppender works with default options. >+ */ >+ public void testDefaultOptions() throws Exception { >+ appender.activateOptions(); >+ log.debug("HI"); >+ appender.close(); >+ BufferedReader r = new BufferedReader(new FileReader(f1)); >+ assertEquals("DEBUG - HI", r.readLine()); >+ } >+ >+} >+ >+ >+ >+ > >Property changes on: tests/src/java/org/apache/log4j/concurrent/FileAppenderTest.java >___________________________________________________________________ >Name: svn:executable > + * > >Index: tests/build.xml >=================================================================== >--- tests/build.xml (revision 463475) >+++ tests/build.xml (working copy) >@@ -225,6 +225,7 @@ > CachedDateFormat, > Encoding, > Syslog, >+ Concurrent, > NTEventLogAppender, > RFA, > ERFA, >@@ -810,6 +811,18 @@ > </junit> > </target> > >+ <target name="Concurrent" depends="check, build"> >+ <junit printsummary="yes" fork="no" haltonfailure="${haltonfailure}"> >+ <classpath refid="tests.classpath"/> >+ <formatter type="plain" usefile="false"/> >+ <test name="org.apache.log4j.concurrent.RollingFileAppenderTest" /> >+ <test name="org.apache.log4j.concurrent.FileAppenderTest" /> >+ <test name="org.apache.log4j.concurrent.ConsoleAppenderTest" /> >+ <test name="org.apache.log4j.concurrent.SynchronizedBooleanTest" /> >+ <test name="org.apache.log4j.concurrent.ConcurrentAppenderTest" /> >+ <test name="org.apache.log4j.concurrent.WriterAppenderTest" /> >+ </junit> >+ </target> > > <!-- ================================================================= --> > <!-- ========================= long Tests ========================= -->
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 24159
:
9633
|
9634
|
9635
|
12840
|
17133
|
17612
|
17613
|
18997
|
18998
| 19004