On Saturday 13 April 2002 20:24, Ceki Gülcü wrote:
> At 18:15 13.04.2002 +0800, Niclas Hedhman wrote:
> >On Saturday 13 April 2002 16:35, Ceki Gülcü wrote:
> > > Hi Niclas,
> > >
> > > This is a pretty neat contribution. Actually Jim Moore made a similar
> > > one some time ago (see contribs/JimMoore).
> > >
> > > The only serious issue with such adapters coccurs when they redirect
> > > System.out and System.err. If log4j encounters an error it will use
> > > LogLog which uses System.out or System.err which in turn will generate
> > > a new log request, which might generate another call to System.out, and
> > > so on, causing an infinite loop.

Ok, here we go;
I have made the changes necessary for detecting infinite loops, but have NOT 
tested it. 
By default, loop detection is not detected. Set the system property 
org.apache.log4j.contrib.StreamAdapter.loopProtection=true to enable that.
It recovers by default by ignoring the message, and returning without 
continuing in the loop, but that can be changed to throw a RuntimeException 
instead, by setting system property 
org.apache.log4j.contrib.StreamAdapter.loopRecovery=exception.
I have tried to make a fairly speedy Lock mechanism, but have also provided 
hat it can be overriden with other implementations, again by setting a system 
property; org.apache.log4j.contrib.StreamAdapter.lockClass, which must
implement the StreamAdapter.Lock interface.

Sorry I don't have time for testing this, and hoping someone else can help 
out.

Regarding the problem about the System.out and System.err redirections 
especially for Logger mechanisms, I fail to see the problem.

The Logger subsystem is initialized very early on startup, and should grab the 
System.out and System.err resources and replace them with their own 
OutputStreams. In that way, logger subsystem can send messages to the 
original System.out and System.err and still have the common System.out and 
System.err redirected to itself.
Anyway, in the days before log4j revelation, that's how I did it, and worked 
pretty well.


Licensing I think is now undoubtful.... ;o)


Niclas Hedhman
/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software
 * License version 1.1, a copy of which has been included with this
 * distribution in the LICENSE.txt file.
 *
 * Contributed by Niclas Hedhman 2002
 */

package org.apache.log4j.contrib;

// To Apache Software Foundation;  Don't know package name guidelines, please change.

import java.io.OutputStream;

import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;

/** Adapter for Streams to be directed to log4j Loggers.
 *
 * <p>This project is using this to redirect the
 * RMI Exception trace to Logger outputs, instead of the
 * System.out/System.err (whichever it may be). However,
 * this adapter is versatile enough to make any kind
 * of OutputStream content to go to the Logger system.</p>
 *
 * <p>Because of the contribution to the Apache Log4J project
 * this class is JDK 1.1 compatible.</p>
 *
 * @author Niclas Hedhman, [EMAIL PROTECTED]
 **/
public class StreamAdapter extends OutputStream
{
    /** The attribute to be set to the <code>setRecoveryMethod</code> method
     * if a <code>RuntimeException</code> should be thrown when a loop is
     * detected.
     */
    public static int RECOVERY_EXCEPTION = 0;
    
    /** The attribute to be set to the <code>setRecoveryMethod</code> method
     * if the message streamed should just be ignored, when a loop is
     * detected.
     */
    public static int RECOVERY_RETURN = 1;

    private static final char NEWLINE = System.getProperty( "line.separator" ).charAt(0);

    private Logger m_Logger;
    private Priority m_Priority;
    private StringBuffer m_Message;

    static private Lock m_LoopLock;
    static private boolean m_LoopProtectionEnabled;
    static private int m_RecoveryMethod;

    static
    {
        String prefix = StreamAdapter.class.getName();
        m_LoopProtectionEnabled = Boolean.getBoolean( prefix + ".loopProtection" );

        if( m_LoopProtectionEnabled )
        {
            String recovery = System.getProperty( prefix + ".loopRecovery" ).toLowerCase();

            if( "exception".equals( recovery )  )
                m_RecoveryMethod = RECOVERY_EXCEPTION;
            else if( "return".equals( recovery )  )
                m_RecoveryMethod = RECOVERY_RETURN;
            else
                m_RecoveryMethod = RECOVERY_RETURN;

            String lockImplClass = System.getProperty( prefix + ".lockClass", "org.apache.log4j.contrib.StreamAdapter$LoopLock" );
            try
            {
                Class cls = Thread.currentThread().getContextClassLoader().loadClass( lockImplClass );
                m_LoopLock = (Lock) cls.newInstance();
            } catch( Exception e )
            {
                // Fail-safe fallback...
                m_LoopLock = new LoopLock();
            }
            m_LoopLock = new StreamAdapter.LoopLock();
        }
    }

    /** Creates the default StreamAdapter.
     *<p>Convinience constructor for
     * <code>StreamAdapter( Logger.getLogger( StreamAdapter.class ),
     * Level.INFO );</code>
     **/
    public StreamAdapter()
    {
        this( Logger.getLogger( StreamAdapter.class ), Level.ERROR );
    }

    /** Creates a StreamAdapter connecting to the given logger.
     *<p>Convinience constructor for
     * <code>StreamAdapter( logger,
     * Level.INFO );</code>
     **/
    public StreamAdapter( Logger logger )
    {
        this( logger, Level.INFO );
    }

    /** Creates a StreamAdapter connecting to a particular
     * logger with a message of a particular priority/level.
     */
    public StreamAdapter( Logger logger, Priority priority )
    {
        m_Logger = logger;
        m_Priority = priority;
        m_Message = new StringBuffer(200);
    }

    public void write( int b )
    {
        if( m_Logger != null && b == NEWLINE )
            flush();
        else
            m_Message.append( (char) b );
    }

    /** Flushes the outputstream and sends the buffered bytes
     * to its destination.
     * <p> This causes the call to the <code>Logger.log</code>
     * method, and will hence constitute a transaction in that
     * system. This method is called whenever a new line character
     * is encountered in the write stream, and upon close.
     **/
    public void flush()
    {
        if( m_LoopProtectionEnabled )
            if( ! m_LoopLock.lock() )
                return;
        if( m_Message.length() > 0 )
        {
            m_Logger.log( m_Priority, m_Message.toString() );
            m_Message.setLength(0);
        }
        if( m_LoopProtectionEnabled )
            m_LoopLock.unlock();
    }

    public void close()
    {
        flush();
    }

    public Logger getLogger()
    {
        return m_Logger;
    }

    public void setLogger( Logger logger )
    {
        m_Logger = logger;
    }

    public void setPriority( Priority priority )
    {
        m_Priority = priority;
    }

    public Priority getPriority()
    {
        return m_Priority;
    }

    public boolean getLoopProtectionEnabled()
    {
        return m_LoopProtectionEnabled;
    }

    public void setLoopProtectionEnabled( boolean enable )
    {
        m_LoopProtectionEnabled = enable;
    }

    public int getRecoveryMethod()
    {
        return m_RecoveryMethod;
    }

    public void setRecoveryMethod( int method )
    {
        if( method != RECOVERY_EXCEPTION && method != RECOVERY_RETURN )
            throw new IllegalArgumentException();

        m_RecoveryMethod = method;
    }

    static public interface Lock
    {
        boolean lock();
        void unlock();
    }

// This is just one way if implementing it. Other strategies can
// be used and compared by setting the
// org.apache.log4j.contrib.StreamAdapter.lockClass to the class
// that implements the lock.
    static private class LoopLock
        implements Lock
    {
        private Thread[] m_Locks;

        private LoopLock()
        {
            m_Locks = new Thread[ 30 ];
        }

        public boolean lock()
        {
            synchronized( this )
            {
                Thread t = Thread.currentThread();
                for( int i=0 ; i < m_Locks.length ; i++ )
                {
                    if( m_Locks[i] == t )
                    {
                        if( m_RecoveryMethod == RECOVERY_EXCEPTION )
                            throw new RuntimeException( "Possible infinite loop detected." );
                        else
                            return false;
                    }
                }
                add( t );
            }
            return true;
        }

        public void unlock()
        {
            synchronized( this )
            {
                Thread t = Thread.currentThread();
                for( int i=0 ; i < m_Locks.length ; i++ )
                {
                    if( m_Locks[i] == t )
                    {
                        m_Locks[i] = null;
                    }
                }
            }
        }

        private void add( Thread t )
        {
            boolean found = false;
            for( int i=0 ; i < m_Locks.length ; i++ )
            {
                if( m_Locks[i] == null )
                {
                    m_Locks[ i ] = t;
                    found = true;
                }
            }
            if( ! found )
            {
                Thread[] newLocks = new Thread[ m_Locks.length * 2 ];
                int len = m_Locks.length;
                System.arraycopy( m_Locks, 0, newLocks, 0, len );
                m_Locks = newLocks;
                m_Locks[len] = t;
            }
        }
    }
}

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to