/**
* Copyright (c) 2014 by the original author or authors.
*
* This code is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package ch.sdi.core.impl.ftp;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.apache.commons.net.SocketClient;
import org.apache.logging.log4j.Logger;
import org.springframework.util.StringUtils;
/**
* Interceptor of FTP commands and responses for writing them to a Log4j2 logger.
* <p>
* This listener creates an internal PrintWriter which can be accessed by getPrintWriter() and which can
* be passed to the constructor of an org.apache.commons.net.PrintCommandListener.PrintCommandListener().
* All lines written to this internal PrintWriter are redirected to the given logger (TRACE-level).
* <p>
* CAUTION: this class is not thread safe.
* <p>
*
* @version 1.0 (22.11.2014)
* @author Heri
*/
public class PrintCommandToLoggerListener
{
private PrintWriter myPrintWriter;
private PrintCommandToLoggerWriter myStringWriter;
public PrintCommandToLoggerListener( Logger aLogger )
{
myStringWriter = new PrintCommandToLoggerWriter( aLogger );
myPrintWriter = new PrintWriter( myStringWriter );
}
/**
* @return myPrintWriter
*/
public PrintWriter getPrintWriter()
{
return myPrintWriter;
}
/**
* Writes the rest of the internal buffer to the log, regardless if there was a line termination or
* not.
*/
public void flushRest()
{
myStringWriter.flushRest();
}
static class PrintCommandToLoggerWriter extends StringWriter
{
private Logger myLog;
/**
* If not null, this internal buffer keeps not yet logged characters (because there wasn't any
* line termination found in the last supplied content). The buffer will be written the next
* time a line termination will be supplied. This ensures that only whole lines are written
* to log as one statement, exactly as if the content would be written to the console.
*/
private StringBuilder myBuffer;
/**
* Constructor
*
* @param aLog
*/
public PrintCommandToLoggerWriter( Logger aLog )
{
super();
myLog = aLog;
}
/**
* @see java.io.StringWriter#write(int)
*/
@Override
public void write( int aInt )
{
getStringBuilder().append( aInt );
}
/**
* @see java.io.StringWriter#write(char[], int, int)
*/
@Override
public void write( char[] cbuf, int off, int len )
{
// this entry check is a copy from base class:
if ( ( off < 0 ) || ( off > cbuf.length ) || ( len < 0 ) || ( ( off + len ) > cbuf.length )
|| ( ( off + len ) < 0 ) )
{
throw new IndexOutOfBoundsException();
}
else if ( len == 0 )
{
return;
}
String str = new String( cbuf, off, len );
sendToLog( str );
}
/**
* @see java.io.StringWriter#write(java.lang.String)
*/
@Override
public void write( String aStr )
{
sendToLog( aStr );
}
/**
* @see java.io.StringWriter#write(java.lang.String, int, int)
*/
@Override
public void write( String aStr, int aOff, int aLen )
{
sendToLog( aStr.substring( aOff, aOff + aLen ) );
}
/**
* @see java.io.StringWriter#append(java.lang.CharSequence)
*/
@Override
public StringWriter append( CharSequence aCsq )
{
sendToLog( aCsq.toString() );
return this;
}
/**
* @see java.io.StringWriter#append(java.lang.CharSequence, int, int)
*/
@Override
public StringWriter append( CharSequence aCsq, int aStart, int aEnd )
{
sendToLog( aCsq.toString().substring( aStart, aStart + aEnd ) );
return this;
}
/**
* @see java.io.StringWriter#append(char)
*/
@Override
public StringWriter append( char aChar )
{
sendToLog( new String( new char[] { aChar } ) );
return this;
}
/**
* @return buffer
*/
public StringBuilder getStringBuilder()
{
if ( myBuffer == null )
{
myBuffer = new StringBuilder();
} // if myBuffer == null
return myBuffer;
}
/**
* @param aString
*/
private void sendToLog( String aString )
{
String str = aString;
if ( !StringUtils.hasText( str ) )
{
if ( SocketClient.NETASCII_EOL.equals( str ) )
{
// sometimes there is only a line termination supplied
writeLineToLog();
return;
} // if SocketClient.NETASCII_EOL.equals( str )
myLog.warn( "given log message has not text" );
return;
} // if !StringUtils.hasText( str )
str = str.replaceAll( SocketClient.NETASCII_EOL, "\n" );
int pos = str.indexOf( "\n" );
while (pos >= 0)
{
// remove leading line terminations:
while ( pos == 0 )
{
str = str.length() > 1 ? str.substring( 1 ) : "";
pos = str.indexOf( "\n" );
}
if ( StringUtils.hasText( str ) )
{
getStringBuilder().append( str.substring(0,pos));
writeLineToLog();
} // if StringUtils.hasText( str )
str = str.length() > (pos+1) ? str.substring( pos+1 ) : "";
pos = str.indexOf( "\n" );
}
if ( StringUtils.hasText( str ) )
{
getStringBuilder().append( str );
} // if StringUtils.hasText( str )
}
/**
*
*/
private void writeLineToLog()
{
myLog.trace( "FTP: " + getStringBuilder().toString() );
myBuffer = null;
}
public void flushRest()
{
if ( myBuffer != null )
{
String s = myBuffer.toString().trim();
if ( StringUtils.hasText( s ) )
{
sendToLog( s + "\n" );
} // if StringUtils.hasText( s )
} // if myBuffer == null
}
/**
* @see java.io.StringWriter#close()
*/
@Override
public void close() throws IOException
{
flushRest();
super.close();
}
}
}