You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-cvs@jakarta.apache.org by ce...@apache.org on 2001/02/16 13:00:31 UTC

cvs commit: jakarta-log4j/contribs/ThomasFenner JDBCAppender.java JDBCConnectionHandler.java JDBCIDHandler.java JDBCLogger.java Log4JTest.java code_example1.java code_example2.java configfile_example.txt

ceki        01/02/16 04:00:29

  Modified:    contribs CONTENTS
  Added:       contribs/CekiGulcu Transform.java
               contribs/ThomasFenner JDBCAppender.java
                        JDBCConnectionHandler.java JDBCIDHandler.java
                        JDBCLogger.java Log4JTest.java code_example1.java
                        code_example2.java configfile_example.txt
  Log:
  Added Thomas Fenner's JDBCAppender and moved Transform.java to contribs/CekiGulcu.
  
  Revision  Changes    Path
  1.2       +26 -0     jakarta-log4j/contribs/CONTENTS
  
  Index: CONTENTS
  ===================================================================
  RCS file: /home/cvs/jakarta-log4j/contribs/CONTENTS,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- CONTENTS	2001/02/14 17:46:57	1.1
  +++ CONTENTS	2001/02/16 12:00:22	1.2
  @@ -1,4 +1,9 @@
   
  +----------------------------------------------------------------------
  +WARNING: The contents of the contribs/ directory is not guaranteed to
  +work properly. Some files might even not compile.
  +----------------------------------------------------------------------
  +
   Each directory corresponds to the name of an author containinghis/her 
   contributions.
   
  @@ -30,6 +35,13 @@
      to be displayed in the logging output. This is important for us as we are 
      using a single logging server with several clients.
   
  +KevinSteppe/
  +===========
  +
  +  JDBCAppender.java
  +  JDBCTest.java
  +   
  +
   KitchingSimon/
   =============
   
  @@ -45,4 +57,18 @@
         (a) the UDP server application does not have to be in java 
         (b) non-java clients can send messages to the same UDP server.
   
  +ThomasFenner/
  +============
  +
  +  Yet one more JDBCAppender.
  +
  +  JDBCAppender.java
  +  JDBCConnectionHandler.java
  +  JDBCIDHandler.java
  +  JDBCLogger.java
  +  Log4JTest.java
  +  code_example1.java
  +  code_example2.java
  +  configfile_example.txt
   
  +  
  \ No newline at end of file
  
  
  
  1.1                  jakarta-log4j/contribs/CekiGulcu/Transform.java
  
  Index: Transform.java
  ===================================================================
  
  package org.apache.log4j.xml;
  
  import org.apache.log4j.Category;
  import org.apache.log4j.Layout;
  import org.apache.log4j.PropertyConfigurator;
  import org.apache.log4j.spi.LoggingEvent;
  import org.apache.log4j.helpers.OptionConverter;
  import org.apache.log4j.helpers.DateLayout;
  
  import org.xml.sax.ContentHandler;
  import org.xml.sax.Locator;
  import org.xml.sax.Attributes;
  import org.xml.sax.XMLReader;
  import org.xml.sax.ext.LexicalHandler;
  import org.xml.sax.helpers.XMLReaderFactory;
  import org.xml.sax.SAXException;
  import org.apache.xerces.parsers.SAXParser;
  
  import org.apache.trax.Processor;
  import org.apache.trax.TemplatesBuilder;
  import org.apache.trax.Templates;
  import org.apache.trax.Transformer;
  import org.apache.trax.Result;
  import org.apache.trax.ProcessorException; 
  import org.apache.trax.ProcessorFactoryException;
  import org.apache.trax.TransformException; 
  
  
  import org.apache.serialize.SerializerFactory;
  import org.apache.serialize.Serializer;
  import org.apache.serialize.OutputFormat;
  import org.xml.sax.helpers.AttributesImpl;
  
  
  import java.io.FileOutputStream;
  import java.io.IOException;
  
  
  public class Transform {
  
    public static void main(String[] args) throws Exception {
      PropertyConfigurator.disableAll();
      PropertyConfigurator.configure("x.lcf");
  
      // I. Instantiate  a stylesheet processor.
      Processor processor = Processor.newInstance("xslt");
  
      // II. Process the stylesheet. producing a Templates object.
  
      // Get the XMLReader.
      XMLReader reader = XMLReaderFactory.createXMLReader();
  
      // Set the ContentHandler.
      TemplatesBuilder templatesBuilder = processor.getTemplatesBuilder();
      reader.setContentHandler(templatesBuilder);
  
      // Set the ContentHandler to also function as a LexicalHandler, which
      // includes "lexical" (e.g., comments and CDATA) events. The Xalan
      // TemplatesBuilder -- org.apache.xalan.processor.StylesheetHandler -- is
      // also a LexicalHandler).
      if(templatesBuilder instanceof LexicalHandler) {
         reader.setProperty("http://xml.org/sax/properties/lexical-handler", 
                             templatesBuilder);
      }
  
      // Parse the stylesheet.                       
      reader.parse(args[0]);
  
      //Get the Templates object from the ContentHandler.
      Templates templates = templatesBuilder.getTemplates();
  
      // III. Use the Templates object to instantiate a Transformer.
      Transformer transformer = templates.newTransformer();
  
      // IV. Perform the transformation.
  
      // Set up the ContentHandler for the output.
  	FileOutputStream fos = new FileOutputStream(args[2]);
      Result result = new Result(fos);
      Serializer serializer = SerializerFactory.getSerializer("xml");
      serializer.setOutputStream(fos);
  
      transformer.setContentHandler(serializer.asContentHandler());
  
      // Set up the ContentHandler for the input.
      org.xml.sax.ContentHandler chandler = transformer.getInputContentHandler();
      DC dc = new DC(chandler);
      reader.setContentHandler(dc);
      if(chandler instanceof LexicalHandler) {
         reader.setProperty("http://xml.org/sax/properties/lexical-handler", 
  			  chandler);
      } else {
         reader.setProperty("http://xml.org/sax/properties/lexical-handler", 
  			  null);
      }
  
      // Parse the XML input document. The input ContentHandler and
      // output ContentHandler work in separate threads to optimize
      // performance.
      reader.parse(args[1]);
    }	
  }
  
   class DC implements ContentHandler {
  
     static Category cat = Category.getInstance("DC");
  
     ContentHandler  chandler;
  
     DC(ContentHandler chandler) {
       this.chandler = chandler;
     }
  
  
    public 
    void characters(char[] ch, int start, int length) 
                              throws org.xml.sax.SAXException {
      cat.debug("characters: ["+new String(ch, start, length)+ "] called");
      chandler.characters(ch, start, length);
  
    }
  
    public 
    void endDocument() throws org.xml.sax.SAXException {
      cat.debug("endDocument called.");
      chandler.endDocument();
  
    }
  
    public 
    void endElement(String namespaceURI, String localName, String qName)
                                             throws org.xml.sax.SAXException {
      cat.debug("endElement("+namespaceURI+", "+localName+", "+qName+") called");
      chandler.endElement(namespaceURI, localName, qName);
    }
     
     public
     void endPrefixMapping(String prefix) throws org.xml.sax.SAXException {
       cat.debug("endPrefixMapping("+prefix+") called");
       chandler.endPrefixMapping(prefix);
     }
  
    public 
    void ignorableWhitespace(char[] ch, int start, int length) 
                                       throws org.xml.sax.SAXException {
      cat.debug("ignorableWhitespace called");
      chandler.ignorableWhitespace(ch, start, length);
    }
    
    public 
    void processingInstruction(java.lang.String target, java.lang.String data) 
                                                throws org.xml.sax.SAXException {
      cat.debug("processingInstruction called");
      chandler.processingInstruction(target, data);
    }
  
    public 
    void setDocumentLocator(Locator locator)  {
      cat.debug("setDocumentLocator called");
      chandler.setDocumentLocator(locator);
    }
  
     public
     void skippedEntity(String name) throws org.xml.sax.SAXException {
       cat.debug("skippedEntity("+name+")  called");
       chandler.skippedEntity(name);     
     }
    
    public 
    void startDocument() throws org.xml.sax.SAXException {
      cat.debug("startDocument called");
      chandler.startDocument();
    }
    
    public 
    void startElement(String namespaceURI, String localName, String qName,
  		    Attributes atts) throws org.xml.sax.SAXException {
      cat.debug("startElement("+namespaceURI+", "+localName+", "+qName+")called");
  
      if("log4j:event".equals(qName)) {
        cat.debug("-------------");      
        if(atts instanceof org.xml.sax.helpers.AttributesImpl) {
  	AttributesImpl ai = (AttributesImpl) atts;
  	int i = atts.getIndex("timestamp");
  	ai.setValue(i, "hello");
        }
        String ts = atts.getValue("timestamp");
        cat.debug("New timestamp is " + ts);
      }
      chandler.startElement(namespaceURI, localName, qName, atts);
    }
  
     public
     void startPrefixMapping(String prefix, String uri) 
                                            throws org.xml.sax.SAXException {
       cat.debug("startPrefixMapping("+prefix+", "+uri+") called");     
       chandler.startPrefixMapping(prefix, uri);
     }
             
     
  }
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/JDBCAppender.java
  
  Index: JDBCAppender.java
  ===================================================================
  
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
  */
  
  package com.klopotek.utils.log;
  
  import java.sql.*;
  import java.util.*;
  import org.apache.log4j.*;
  import org.apache.log4j.helpers.*;
  import org.apache.log4j.spi.*;
  
  /**
  The JDBCAppender, writes messages into a database
  
  <p><b>The JDBCAppender is configurable at runtime by setting options in two alternatives : </b></p>
  <dir>
  	<p><b>1. Use a configuration-file</b></p>
  	<p>Define the options in a file (<A HREF="configfile_example.txt">example</A>) and call a <code>PropertyConfigurator.configure(filename)</code> in your code.</p>
  	<p><b>2. Use the methods of JDBCAppender to do it</b></p>
  	<p>Call <code>JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)</code> to do it analogically without a configuration-file (<A HREF="code_example2.java">example</A>)</p>
  </dir>
  
  <p>All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.</p>
  
  <p><b>Here is a description of all available options :</b></p>
  <dir>
  	<p><b>1. Database-options to connect to the database</b></p>
  	<p>- <b>URL_OPTION</b>			: a database url of the form jdbc:subprotocol:subname</p>
  	<p>- <b>USERNAME_OPTION</b>	: the database user on whose behalf the connection is being made</p>
  	<p>- <b>PASSWORD_OPTION</b>	: the user's password</p>
  
  	<p><b>2. Connector-option to specify your own JDBCConnectionHandler</b></p>
  	<p>- <b>CONNECTOR_OPTION</b>	: a classname which is implementing the JDBCConnectionHandler-interface</p>
  	<p>This interface is used to get a customized connection.</p>
  	<p>If in addition the database-options are given, these options will be used as arguments for the JDBCConnectionHandler-interface to get a connection.</p>
  	<p>Else if no database-options are given, the JDBCConnectionHandler-interface is called without them.</p>
  	<p>Else if this option is not defined, the database-options are required to open a connection by the JDBCAppender.</p>
  
  	<p><b>3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event</b></p>
  	<p>- <b>SQL_OPTION</b>			: a sql-statement which will be used to write to the database</p>
  	<p>Use the variable <b>@MSG@</b> on a location in the statement, which has to be dynamically replaced by the message-text.</p>
  	<p>If you give this option, the table-option and columns-option will be ignored !</p>
  
  	<p><b>4. Table-option to specify a table contained by the database</b></p>
  	<p>- <b>TABLE_OPTION</b>		: the table in which the logging will be done</p>
  
  	<p><b>5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)</b></p>
  	<p>- <b>COLUMNS_OPTION</b>		: a formatted list of column-descriptions</p>
  	<p>Each column description consists of</p>
  	<dir>
  		<p>- the <b><i>name</i></b> of the column (required)</p>
  		<p>- a <b><i>logtype</i></b> which is a static constant of class LogType (required)</p>
  		<p>- and a <b><i>value</i></b> which depends by the LogType (optional/required, depending by logtype)</p>
  	</dir>
  	<p>Here is a description of the available logtypes of class <b>{@link LogType}</b> and how to handle the <b><i>value</i></b>:</p>
  	<dir>
  		<p>o <b>MSG</b>			= a value will be ignored, the column will get the message. (One columns need to be of this type!)</p>
  		<p>o <b>STATIC</b>		= the value will be filled into the column with every logged message. (Ensure that the type of value can be casted into the sql-type of the column!)</p>
  		<p>o <b>ID</b>			= value must be a classname, which implements the JDBCIDHandler-interface.</p>
  		<p>o <b>TIMESTAMP</b>	= a value will be ignored, the column will be filled with a actually timestamp with every logged message.</p>
  		<p>o <b>EMPTY</b>		= a value will be ignored, the column will be ignored when writing to the database (Ensure to fill not nullable columns by a database trigger!)</p>
  	</dir>
  	<p>If there are more than one column to describe, the columns must be separated by a Tabulator-delimiter (unicode0008) !</p>
  	<p>The arguments of a column-description must be separated by the delimiter '~' !</p>
  	<p><i>(Example :  name1~logtype1~value1   name2~logtype2~value2...)</i></p>
  
  	<p><b>6. Layout-options to define the layout of the messages (optional)</b></p>
  	<p>- <b>_</b> : the layout wont be set by a xxx_OPTION</p>
  	<p>See the configuration-file and code examples below...</p>
  	<p>The default is a layout of the class {@link org.apache.log4j.PatternLayout} with the pattern=%m which representate only the message.</p>
  
  	<p><b>7. Buffer-option to define the size of the message-event-buffer (optional)</b></p>
  	<p>- <b>BUFFER_OPTION</b>		: define how many messages will be buffered until they will be updated to the database.</p>
  	<p>The default is buffer=1, which will do a update with every happened message-event.</p>
  
  	<p><b>8. Commit-option to define a auto-commitment</b></p>
  	<p>- <b>COMMIT_OPTION</b>		: define whether updated messages should be committed to the database (Y) or not (N).</p>
  	<p>The default is commit=Y.</p>
  </dir>
  
  <p><b>The sequence of some options is important :</b></p>
  <dir>
  	<p><b>1. Connector-option OR/AND Database-options</b></p>
  	<p>Any database connection is required !</p>
  	<p><b>2. (Table-option AND Columns-option) OR SQL-option</b></p>
  	<p>Anything of that is required ! Whether where to write something OR what to write somewhere...;-)</p>
  	<p><b>3. All other options can be set at any time...</b></p>
  	<p>The other options are optional and have a default initialization, which can be customized.</p>
  </dir>
  
  <p><b>Here is a <b>configuration-file example</b>, which can be used as argument for the <b>PropertyConfigurator</b> : </b><A HREF="configfile_example.txt"> configfile_example.txt</A></p>
  
  <p><b>Here is a <b>code-example</b> to configure the JDBCAppender <b>with a configuration-file</b> : </b><A HREF="code_example1.java"> code_example1.java</A></p>
  
  <p><b>Here is a <b>another code-example</b> to configure the JDBCAppender <b>without a configuration-file</b> : </b><A HREF="code_example2.java"> code_example2.java</A></p>
  
  
  
  <p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
  
  @since 1.0
  */
  public class JDBCAppender extends AppenderSkeleton
  {
  	/**
  	A database-option to to set a database url of the form jdbc:subprotocol:subname.
  	*/
  	public static final String URL_OPTION			= "url";
  
  	/**
  	A database-option to set the database user on whose behalf the connection is being made.
  	*/
  	public static final String USERNAME_OPTION	= "username";
  
  	/**
  	A database-option to set the user's password.
  	*/
  	public static final String PASSWORD_OPTION	= "password";
  
  	/**
  	A table-option to specify a table contained by the database
  	*/
  	public static final String TABLE_OPTION		= "table";
  
  	/**
  	A connector-option to specify your own JDBCConnectionHandler
  	*/
  	public static final String CONNECTOR_OPTION	= "connector";
  
  	/**
     A columns-option to describe the important columns of the table
  	*/
  	public static final String COLUMNS_OPTION		= "columns";
  
  	/**
  	A sql-option to specify a static sql-statement which will be performed with every occuring message-event
     */
  	public static final String SQL_OPTION			= "sql";
  
  	/**
     A buffer-option to define the size of the message-event-buffer
  	*/
  	public static final String BUFFER_OPTION		= "buffer";
  
  	/**
     A commit-option to define a auto-commitment
  	*/
  	public static final String COMMIT_OPTION		= "commit";
  
  
  	//Variables to store the options values setted by setOption() :
  	private String url		= null;
  	private String username	= null;
  	private String password	= null;
  	private String table		= null;
  	private String connection_class = null;
  	private String sql		= null;
  	private boolean docommit = true;
  	private int buffer_size	= 1;
     private JDBCConnectionHandler connectionHandler = null;
  
  	//This buffer stores message-events.
     //When the buffer_size is reached, the buffer will be flushed and the messages will updated to the database.
  	private ArrayList buffer = new ArrayList();
  
     //Database-connection
  	private Connection con = null;
  
  	//This class encapsulate the logic which is necessary to log into a table
  	private JDBCLogger jlogger = new JDBCLogger();
  
     //Flags :
     //A flag to indicate a established database connection
  	private boolean connected = false;
     //A flag to indicate configuration status
  	private boolean configured = false;
     //A flag to indicate that everything is ready to get append()-commands.
  	private boolean ready = false;
  
  	/**
  	If program terminates close the database-connection and flush the buffer
     */
  	public void finalize()
  	{
  		close();
        super.finalize();
  	}
  
  	/**
  	Internal method. Returns a array of strings containing the available options which can be set with method setOption()
  	*/
  	public String[] getOptionStrings()
     {
     	// The sequence of options in this string is important, because setOption() is called this way ...
  		return new String[]{CONNECTOR_OPTION, URL_OPTION, USERNAME_OPTION, PASSWORD_OPTION, SQL_OPTION, TABLE_OPTION, COLUMNS_OPTION, BUFFER_OPTION, COMMIT_OPTION};
  	}
  
  
  	/**
       Sets all necessary options
  	*/
  	public void setOption(String _option, String _value)
  	{
     	_option = _option.trim();
        _value = _value.trim();
  
  		if(_option == null || _value == null) return;
  		if(_option.length() == 0 || _value.length() == 0) return;
  
        _value = _value.trim();
  
  		if(_option.equals(CONNECTOR_OPTION))
        {
        	if(!connected) connection_class = _value;
        }
  		else if(_option.equals(URL_OPTION))
  		{
  			if(!connected) url = _value;
  		}
  		else if(_option.equals(USERNAME_OPTION))
  		{
  			if(!connected) username = _value;
  		}
  		else if(_option.equals(PASSWORD_OPTION))
  		{
  			if(!connected) password = _value;
  		}
  		else if(_option.equals(SQL_OPTION))
        {
  			sql = _value;
        }
  		else if(_option.equals(TABLE_OPTION))
        {
        	if(sql != null) return;
        	table = _value;
        }
  		else if(_option.equals(COLUMNS_OPTION))
        {
        	if(sql != null) return;
  
  			String name = null;
           int logtype = -1;
           String value = null;
           String column = null;
           String arg = null;
           int num_args = 0;
           int num_columns = 0;
  			StringTokenizer st_col;
  			StringTokenizer st_arg;
  
           //Columns are TAB-separated
  			st_col = new StringTokenizer(_value,  "	");
  
  			num_columns = st_col.countTokens();
  
           if(num_columns < 1)
    	      {
       	   	errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
              return;
          	}
  
           for(int i=1; i<=num_columns; i++)
           {
  				column = st_col.nextToken();
  
              //Arguments are ~-separated
  				st_arg = new StringTokenizer(column, "~");
  
  				num_args = st_arg.countTokens();
  
  	         if(num_args < 2)
     	      {
        	   	errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
                 return;
           	}
  
  	         for(int j=1; j<=num_args; j++)
     	      {
  					arg = st_arg.nextToken();
  
  					if(j == 1) name = arg;
  					else if(j == 2)
        	      {
           	   	try
              	   {
  							logtype = Integer.parseInt(arg);
  	               }
     	            catch(Exception e)
        	         {
           	      	logtype = LogType.parseLogType(arg);
  	               }
  
  						if(!LogType.isLogType(logtype))
     	            {
  	   	            errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION LogType : " + arg + " !");
                       return;
           	      }
              	}
  					else if(j == 3) value = arg;
     	      }
  
  	         if(!setLogType(name, logtype, value)) return;
           }
        }
  		else if(_option.equals(BUFFER_OPTION))
        {
          	try
           {
  				buffer_size = Integer.parseInt(_value);
           }
           catch(Exception e)
           {
  	         errorHandler.error("JDBCAppender::setOption(), Invalid BUFFER_OPTION value : " + _value + " !");
  				return;
           }
        }
  		else if(_option.equals(COMMIT_OPTION))
        {
        	docommit = _value.equals("Y");
        }
  
        if(_option.equals(SQL_OPTION) || _option.equals(TABLE_OPTION))
        {
  			if(!configured) configure();
        }
  	}
  
  	/**
  	Internal method. Returns true, you may define your own layout...
  	*/
  	public boolean requiresLayout()
  	{
  		return true;
  	}
  
  
  	/**
  	Internal method. Close the database connection & flush the buffer.
  	*/
  	public void close()
  	{
  	   flush_buffer();
        if(connection_class == null)
        {
  			try{con.close();}catch(Exception e){errorHandler.error("JDBCAppender::close(), " + e);}
        }
  		this.closed = true;
  	}
  
  
  	/**
  	You have to call this function for all provided columns of your log-table !
     */
  	public boolean setLogType(String _name, int _logtype, Object _value)
  	{
     	if(sql != null) return true;
  
  		if(!configured)
  		{
  			if(!configure()) return false;
  		}
  
  		try
  		{
  			jlogger.setLogType(_name, _logtype, _value);
  		}
  		catch(Exception e)
  		{
  			errorHandler.error("JDBCAppender::setLogType(), " + e);
  			return false;
  		}
  
  		return true;
  	}
  
  
  	/**
  	Internal method. Appends the message to the database table.
  	*/
  	public void append(LoggingEvent event)
  	{
  		if(!ready)
        {
        	if(!ready())
           {
  				errorHandler.error("JDBCAppender::append(), Not ready to append !");
           	return;
  			}
        }
  
  		buffer.add(event);
  
  		if(buffer.size() >= buffer_size) flush_buffer();
  	}
  
  
  	/**
  	Internal method. Flushes the buffer.
  	*/
     public void flush_buffer()
     {
     	try
        {
        	int size = buffer.size();
  
           if(size < 1) return;
  
          	for(int i=0; i<size; i++)
           {
  				LoggingEvent event = (LoggingEvent)buffer.get(i);
  
  				//Insert message into database
  				jlogger.append(layout.format(event));
           }
  
           buffer.clear();
  
  			if(docommit) con.commit();
        }
  		catch(Exception e)
  		{
  			errorHandler.error("JDBCAppender::flush_buffer(), " + e + " : " + jlogger.getErrorMsg());
  			try{con.rollback();} catch(Exception ex){}
  			return;
  		}
     }
  
  
  	/**
  	Internal method. Returns true, when the JDBCAppender is ready to append messages to the database, else false.
  	*/
  	public boolean ready()
  	{
     	if(ready) return true;
  
  		if(!configured) return false;
  
  		ready = jlogger.ready();
  
        if(!ready){errorHandler.error(jlogger.getErrorMsg());}
  
        return ready;
  	}
  
  
  	/**
  	Internal method. Connect to the database.
  	*/
  	protected void connect() throws Exception
  	{
     	if(connected) return;
  
  		try
  		{
        	if(connection_class == null)
           {
  				if(url == null)		throw new Exception("JDBCAppender::connect(), No URL defined.");
  
  				if(username == null)	throw new Exception("JDBCAppender::connect(), No USERNAME defined.");
  
  				if(password == null)	throw new Exception("JDBCAppender::connect(), No PASSWORD defined.");
  
  				connectionHandler = new DefaultConnectionHandler();
  			}
           else
           {
  				connectionHandler = (JDBCConnectionHandler)(Class.forName(connection_class).newInstance());
           }
  
           if(url != null && username != null && password != null)
           {
  				con = connectionHandler.getConnection(url, username, password);
           }
           else
           {
  	     		con = connectionHandler.getConnection();
           }
  
           if(con.isClosed())
           {
           	throw new Exception("JDBCAppender::connect(), JDBCConnectionHandler returns no connected Connection !");
  			}
  		}
  		catch(Exception e)
  		{
  			throw new Exception("JDBCAppender::connect(), " + e);
  		}
  
        connected = true;
  	}
  
  	/**
  	Internal method. Configures for appending...
  	*/
  	protected boolean configure()
  	{
  		if(configured) return true;
  
  		if(!connected)
  		{
        	if((connection_class == null) && (url == null || username == null || password == null))
  			{
  				errorHandler.error("JDBCAppender::configure(), Missing database-options or connector-option !");
  				return false;
           }
  
           try
           {
  				connect();
           }
           catch(Exception e)
           {
           	connection_class = null;
              url = null;
  				errorHandler.error("JDBCAppender::configure(), " + e);
              return false;
           }
  		}
  
  		if(sql == null && table == null)
  		{
  			errorHandler.error("JDBCAppender::configure(), No SQL_OPTION or TABLE_OPTION given !");
  			return false;
  		}
  
  		if(!jlogger.isConfigured())
  		{
  			try
           {
           	jlogger.setConnection(con);
  
           	if(sql == null)
              {
  	         	jlogger.configureTable(table);
              }
              else jlogger.configureSQL(sql);
           }
           catch(Exception e)
           {
  	         errorHandler.error("JDBCAppender::configure(), " + e);
           	return false;
           }
  		}
  
        //Default Message-Layout
        if(layout == null)
        {
        	layout = new PatternLayout("%m");
        }
  
        configured = true;
  
  		return true;
  	}
  }
  
  /**
  This is a default JDBCConnectionHandler used by JDBCAppender
  */
  class DefaultConnectionHandler implements JDBCConnectionHandler
  {
  	Connection con = null;
  
     public Connection getConnection()
     {
     	return con;
     }
  
     public Connection getConnection(String _url, String _username, String _password)
     {
     	try
        {
     		if(con != null && !con.isClosed()) con.close();
  			con = DriverManager.getConnection(_url, _username, _password);
  			con.setAutoCommit(false);
        }
        catch(Exception e){}
  
     	return con;
     }
  }
  
  
  
  
  
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/JDBCConnectionHandler.java
  
  Index: JDBCConnectionHandler.java
  ===================================================================
  
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
  */
  
  package com.klopotek.utils.log;
  
  import java.sql.*;
  
  
  /**
  This interface has to be implemented for your own database-connection-handling and its used in class JDBCLogger.
  
  <p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
  
  @since 1.0
  */
  public interface JDBCConnectionHandler
  {
  	/**Get a connection*/
  	Connection getConnection();
  	/**Get a defined connection*/
     Connection getConnection(String _url, String _username, String _password);
  }
  
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/JDBCIDHandler.java
  
  Index: JDBCIDHandler.java
  ===================================================================
  
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
  */
  
  package com.klopotek.utils.log;
  
  
  /**
  This interface has to be implemented to provide ID-columns with unique IDs and its used in class JDBCLogger.
  
  <p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
  
  @since 1.0
  */
  public interface JDBCIDHandler
  {
  	/**Get a unique ID*/
  	Object getID();
  }
  
  
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/JDBCLogger.java
  
  Index: JDBCLogger.java
  ===================================================================
  
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
  */
  
  package com.klopotek.utils.log;
  
  import java.sql.*;
  import java.util.*;
  import org.apache.log4j.*;
  import org.apache.log4j.helpers.*;
  import org.apache.log4j.spi.*;
  
  
  /**
  This class encapsulate the logic which is necessary to log into a table.
  Used by JDBCAppender
  
  <p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
  
  @since 1.0
  */
  public class JDBCLogger
  {
  	//All columns of the log-table
  	private ArrayList logcols = null;
     //Only columns which will be provided by logging
     private String column_list = null;
     //Number of all columns
  	private int num = 0;
     //Status for successful execution of method configure()
  	private boolean isconfigured = false;
     //Status for ready to do logging with method append()
  	private boolean ready = false;
     //This message will be filled with a error-string when method ready() failes, and can be got by calling getMsg()
     private String errormsg = "";
  
  	private Connection con = null;
  	private Statement stmt = null;
  	private ResultSet rs = null;
     private String table = null;
  
     //Variables for static SQL-statement logging
     private String sql = null;
  	private String new_sql = null;
     private String new_sql_part1 = null;
     private String new_sql_part2 = null;
     private static final String msg_wildcard = "@MSG@";
  	private int msg_wildcard_pos = 0;
  
  	/**
  	Writes a message into the database table.
  	Throws an exception, if an database-error occurs !
  	*/
  	public void append(String _msg) throws Exception
  	{
  		if(!ready) if(!ready()) throw new Exception("JDBCLogger::append(), Not ready to append !");
  
        if(sql != null)
        {
        	appendSQL(_msg);
           return;
        }
  
  		LogColumn logcol;
  
  		rs.moveToInsertRow();
  
  		for(int i=0; i<num; i++)
  		{
          	logcol = (LogColumn)logcols.get(i);
  
  			if(logcol.logtype == LogType.MSG)
  			{
  				rs.updateObject(logcol.name, _msg);
  			}
  			else if(logcol.logtype == LogType.ID)
  			{
  				rs.updateObject(logcol.name, logcol.idhandler.getID());
  			}
  			else if(logcol.logtype == LogType.STATIC)
  			{
  				rs.updateObject(logcol.name, logcol.value);
  			}
  			else if(logcol.logtype == LogType.TIMESTAMP)
  			{
  				rs.updateObject(logcol.name, new Timestamp((new java.util.Date()).getTime()));
  			}
  		}
  
  		rs.insertRow();
  	}
  
  	/**
  	Writes a message into the database using a given sql-statement.
  	Throws an exception, if an database-error occurs !
  	*/
  	public void appendSQL(String _msg) throws Exception
  	{
  		if(!ready) if(!ready()) throw new Exception("JDBCLogger::appendSQL(), Not ready to append !");
  
        if(sql == null) throw new Exception("JDBCLogger::appendSQL(), No SQL-Statement configured !");
  
        if(msg_wildcard_pos > 0)
        {
  			new_sql = new_sql_part1 + _msg + new_sql_part2;
        }
  		else new_sql = sql;
  
        try
        {
  			stmt.executeUpdate(new_sql);
        }
        catch(Exception e)
        {
        	errormsg = new_sql;
           throw e;
  		}
  	}
  
  
  	/**
  	Configures this class, by reading in the structure of the log-table
  	Throws an exception, if an database-error occurs !
  	*/
  	public void configureTable(String _table) throws Exception
  	{
     	if(isconfigured) return;
  
  		//Fill logcols with META-informations of the table-columns
  		stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
  		rs = stmt.executeQuery("SELECT * FROM " + _table + " WHERE 1 = 2");
  
  		LogColumn logcol;
  
  		ResultSetMetaData rsmd = rs.getMetaData();
  
  		num = rsmd.getColumnCount();
  
  		logcols = new ArrayList(num);
  
  		for(int i=1; i<=num; i++)
  		{
  			logcol = new LogColumn();
  			logcol.name = rsmd.getColumnName(i).toUpperCase();
  			logcol.type = rsmd.getColumnTypeName(i);
  			logcol.nullable = (rsmd.isNullable(i) == rsmd.columnNullable);
           logcol.isWritable = rsmd.isWritable(i);
           if(!logcol.isWritable) logcol.ignore = true;
           logcols.add(logcol);
  		}
  
        table = _table;
  
  		isconfigured = true;
  	}
  
  	/**
  	Configures this class, by storing and parsing the given sql-statement.
  	Throws an exception, if somethings wrong !
  	*/
  	public void configureSQL(String _sql) throws Exception
  	{
     	if(isconfigured) return;
  
  		if(!isConnected()) throw new Exception("JDBCLogger::configureSQL(), Not connected to database !");
  
  		if(_sql == null || _sql.trim().equals("")) throw new Exception("JDBCLogger::configureSQL(), Invalid SQL-Statement !");
  
  		sql = _sql.trim();
  
        stmt = con.createStatement();
  
  		msg_wildcard_pos = sql.indexOf(msg_wildcard);
  
        if(msg_wildcard_pos > 0)
        {
  			new_sql_part1 = sql.substring(0, msg_wildcard_pos-1) + "'";
           //between the message...
           new_sql_part2 = "'" + sql.substring(msg_wildcard_pos+msg_wildcard.length());
  		}
  
  		isconfigured = true;
  	}
  
  	/**
     Sets a connection. Throws an exception, if the connection is not open !
  	*/
  	public void setConnection(Connection _con) throws Exception
  	{
  		con = _con;
  
  		if(!isConnected()) throw new Exception("JDBCLogger::setConnection(), Given connection isnt connected to database !");
  	}
  
  
  	/**
  	Sets a columns logtype (LogTypes) and value, which depends on that logtype.
  	Throws an exception, if the given arguments arent correct !
     */
  	public void setLogType(String _name, int _logtype, Object _value) throws Exception
  	{
  		if(!isconfigured) throw new Exception("JDBCLogger::setLogType(), Not configured !");
  
  		//setLogType() makes only sense for further configuration of configureTable()
        if(sql != null) return;
  
        _name = _name.toUpperCase();
  
  		if(_name == null || !(_name.trim().length() > 0)) throw new Exception("JDBCLogger::setLogType(), Missing argument name !");
  		if(!LogType.isLogType(_logtype)) throw new Exception("JDBCLogger::setLogType(), Invalid logtype '" + _logtype + "' !");
  		if((_logtype != LogType.MSG && _logtype != LogType.EMPTY) && _value == null) throw new Exception("JDBCLogger::setLogType(), Missing argument value !");
  
    		LogColumn logcol;
  
  		for(int i=0; i<num; i++)
  		{
          	logcol = (LogColumn)logcols.get(i);
  
  			if(logcol.name.equals(_name))
  			{
           	if(!logcol.isWritable) throw new Exception("JDBCLogger::setLogType(), Column " + _name + " is not writeable !");
  
  				//Column gets the message
  				if(_logtype == LogType.MSG)
              {
              	logcol.logtype = _logtype;
                 return;
  				}
  				//Column will be provided by JDBCIDHandler::getID()
  				else if(_logtype == LogType.ID)
  				{
  					logcol.logtype = _logtype;
  
                 try
                 {
                 	//Try to cast directly Object to JDBCIDHandler
  						logcol.idhandler = (JDBCIDHandler)_value;
                 }
                 catch(Exception e)
                 {
                 	try
                    {
                    	//Assuming _value is of class string which contains the classname of a JDBCIDHandler
  							logcol.idhandler = (JDBCIDHandler)(Class.forName((String)_value).newInstance());
                    }
                    catch(Exception e2)
                    {
  							throw new Exception("JDBCLogger::setLogType(), Cannot cast value of class " + _value.getClass() + " to class JDBCIDHandler !");
                    }
                 }
  
                 return;
  				}
  
  				//Column will be statically defined with Object _value
  				else if(_logtype == LogType.STATIC)
  				{
  					logcol.logtype = _logtype;
  					logcol.value = _value;
                 return;
  				}
  
  				//Column will be provided with a actually timestamp
  				else if(_logtype == LogType.TIMESTAMP)
  				{
  					logcol.logtype = _logtype;
                 return;
  				}
  
              //Column will be fully ignored during process.
              //If this column is not nullable, the column has to be filled by a database trigger,
              //else a database error occurs !
              //Columns which are not nullable, but should be not filled, must be explicit assigned with LogType.EMPTY,
              //else a value is required !
  				else if(_logtype == LogType.EMPTY)
  				{
  					logcol.logtype = _logtype;
  					logcol.ignore = true;
                 return;
  				}
  			}
  		}
  	}
  
  
  	/**
  	Return true, if this class is ready to append(), else false.
  	When not ready, a reason-String is stored in the instance-variable msg.
  	*/
  	public boolean ready()
  	{
     	if(ready) return true;
  
  		if(!isconfigured){ errormsg = "Not ready to append ! Call configure() first !"; return false;}
  
        //No need to doing the whole rest...
        if(sql != null)
        {
        	ready = true;
           return true;
        }
  
  		boolean msgcol_defined = false;
  
  		LogColumn logcol;
  
  		for(int i=0; i<num; i++)
  		{
        	logcol = (LogColumn)logcols.get(i);
  
           if(logcol.ignore || !logcol.isWritable) continue;
  			if(!logcol.nullable && logcol.logtype == LogType.EMPTY)
           {
           	errormsg = "Not ready to append ! Column " + logcol.name + " is not nullable, and must be specified by setLogType() !";
              return false;
           }
  			if(logcol.logtype == LogType.ID && logcol.idhandler == null)
           {
           	errormsg = "Not ready to append ! Column " + logcol.name + " is specified as an ID-column, and a JDBCIDHandler has to be set !";
              return false;
           }
  			else if(logcol.logtype == LogType.STATIC && logcol.value == null)
           {
           	errormsg = "Not ready to append ! Column " + logcol.name + " is specified as a static field, and a value has to be set !";
              return false;
           }
           else if(logcol.logtype == LogType.MSG) msgcol_defined = true;
  		}
  
        if(!msgcol_defined) return false;
  
        //create the column_list
  		for(int i=0; i<num; i++)
  		{
        	logcol = (LogColumn)logcols.get(i);
  
  			if(logcol.ignore || !logcol.isWritable) continue;
  
           if(logcol.logtype != LogType.EMPTY)
           {
  				if(column_list == null)
              {
              	column_list = logcol.name;
              }
              else column_list += ", " + logcol.name;
           }
  		}
  
        try
        {
  			rs = stmt.executeQuery("SELECT " + column_list + " FROM " + table + " WHERE 1 = 2");
  		}
        catch(Exception e)
        {
  			errormsg = "Not ready to append ! Cannot select columns '" + column_list + "' of table " + table + " !";
        	return false;
        }
  
  		ready = true;
  
  		return true;
  	}
  
  	/**
  	Return true, if this class is configured, else false.
  	*/
  	public boolean isConfigured(){ return isconfigured;}
  
  	/**
  	Return true, if this connection is open, else false.
  	*/
  	public boolean isConnected()
     {
     	try
        {
     		return (con != null && !con.isClosed());
        }
        catch(Exception e){return false;}
     }
  
  	/**
  	Return the internal error message stored in instance variable msg.
  	*/
     public String getErrorMsg(){String r = new String(errormsg); errormsg = null; return r;}
  }
  
  
  /**
  This class encapsulate all by class JDBCLogger needed data around a column
  */
  class LogColumn
  {
  	//column name
  	String name = null;
     //column type
  	String type = null;
     //not nullability means that this column is mandatory
  	boolean nullable = false;
     //isWritable means that the column can be updated, else column is only readable
     boolean isWritable = false;
     //if ignore is true, this column will be ignored by building sql-statements.
     boolean ignore = false;
  
  	//Must be filled for not nullable columns ! In other case it is optional.
  	int logtype = LogType.EMPTY;
  	Object value = null;				//Generic storage for typewrapper-classes Long, String, etc...
  	JDBCIDHandler idhandler = null;
  }
  
  
  /**
  This class contains all constants which are necessary to define a columns log-type.
  */
  class LogType
  {
  	//A column of this type will receive the message.
  	public static final int MSG = 1;
  
  	//A column of this type will be a unique identifier of the logged row.
  	public static final int ID = 2;
  
  	//A column of this type will contain a static, one-time-defined value.
  	public static final int STATIC = 3;
  
    	//A column of this type will be filled with an actual timestamp depending by the time the logging will be done.
  	public static final int TIMESTAMP = 4;
  
  	//A column of this type will contain no value and will not be included in logging insert-statement.
     //This could be a column which will be filled not by creation but otherwhere...
  	public static final int EMPTY = 5;
  
  
  	public static boolean isLogType(int _lt)
  	{
  		if(_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP || _lt == EMPTY) return true;
  
  		return false;
  	}
  
     public static int parseLogType(String _lt)
     {
  		if(_lt.equals("MSG")) return MSG;
  		if(_lt.equals("ID")) return ID;
  		if(_lt.equals("STATIC")) return STATIC;
  		if(_lt.equals("TIMESTAMP")) return TIMESTAMP;
  		if(_lt.equals("EMPTY")) return EMPTY;
  
        return -1;
     }
  }
  
  
  
  
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/Log4JTest.java
  
  Index: Log4JTest.java
  ===================================================================
  /**
  // Class JDBCAppender, writes messages into a database
  
  // The JDBCAppender is configurable at runtime in two alternatives :
  // 1. Configuration-file
  //    Define the options in a file and call a PropertyConfigurator.configure(file)-method.
  // 2. method JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)
  
  // The sequence of some options is important :
  // 1. Connector-option OR/AND Database-options
  //    Any database connection is required !
  // 2. (Table-option AND Columns-option) OR SQL-option
  //		Any statement is required !
  // 3. All other options can be set at any time...
  //    The other options are optional and have a default initialization, which can be custumized.
  
  // All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.
  
  // Here is a description of all available options :
  // 1. Database-options to connect to the database
  //    - URL_OPTION			: a database url of the form jdbc:subprotocol:subname
  //    - USERNAME_OPTION		: the database user on whose behalf the connection is being made
  //    - PASSWORD_OPTION		: the user's password
  //
  // 2. Connector-option to specify your own JDBCConnectionHandler
  //    - CONNECTOR_OPTION	: a classname which is implementing the JDBCConnectionHandler-interface
  //    This interface is used to get a customized connection.
  //    If in addition the database-options are given, these options will be used
  //    for invocation of the JDBCConnectionHandler-interface to get a connection.
  //    Else if no database-options are given, the JDBCConnectionHandler-interface is called without these options.
  //
  // 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
  //    - SQL_OPTION			: a sql-statement which will be used to write to the database
  //    If you give this option, the table-option and columns-option will be ignored !
  //    Use the variable @MSG@ on that location in the statement, which has to be dynamically replaced by the message.
  //
  // 4. Table-option to specify one table contained by the database
  //    - TABLE_OPTION			: the table in which the logging will be done
  //
  // 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
  //    - COLUMNS_OPTION		: a formatted list of column-descriptions
  //    Each column description consists of
  //       - the name of the column (required)
  //			- a logtype which is a static constant of class LogType (required)
  //       - and a value which depends by the LogType (optional/required, depending by logtype)
  //    Here is a description of the available logtypes of class LogType :
  //       o MSG			= a value will be ignored, the column will get the message. (One columns need to be of this type!)
  //       o STATIC		= the value will be filled into the column with every logged message. (Ensure that the type of value can be casted into the sql-type of the column!)
  //       o ID			= value must be a classname, which implements the JDBCIDHandler-interface.
  //       o TIMESTAMP	= a value will be ignored, the column will be filled with a actually timestamp with every logged message.
  //       o EMPTY		= a value will be ignored, the column will be ignored when writing to the database (Ensure to fill not nullable columns by a database trigger!)
  //    If there are more than one column to describe, the columns must be separated by a TAB-delimiter ('	') !
  //    The arguments of a column-description must be separated by the delimiter '~' !
  //		(Example :  name1~logtype1~value1	name2~logtype2~value2...)
  //
  // 6. Layout-options to define the layout of the messages (optional)
  //    - the layout wont be set by a xxx_OPTION
  //    Configuration-file			: see at the following configuration-file example
  //    JDBCAppender::setOption()	: see at the following code example
  //    The default is a layout of class org.apache.log4j.PatternLayout with ConversionPattern=%m
  //
  // 7. Buffer-option to define the size of the message-event-buffer (optional)
  //    - BUFFER_OPTION		: define how many messages will be buffered until they will be updated to the database.
  //    The default is a update on every message (buffer=1=no buffer).
  //
  // 8. Commit-option to define a auto-commitment
  //    - COMMIT_OPTION		: define whether updated messages should be committed to the database (Y) or not (N).
  //    The default is a commit on every buffer-flush.
  
  
  // Here is a Configuration-file example, which can be used with the PropertyConfigurator :
  //
  // Declare a appender variable named JDBC
  log4j.rootCategory=JDBC
  
  // JDBC is a class of JDBCAppender, which writes messages into a database
  log4j.appender.JDBC=JDBCAppender
  
  // 1. Database-options to connect to the database
  log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
  log4j.appender.JDBC.username=mex_pr_dev60
  log4j.appender.JDBC.password=mex_pr_dev60
  
  // 2. Connector-option to specify your own JDBCConnectionHandler
  log4j.appender.JDBC.connector=MyConnectionHandler
  
  // 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
  log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
  
  // 4. Table-option to specify one table contained by the database
  log4j.appender.JDBC.table=logtest
  
  // 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
  log4j.appender.JDBC.columns=id_seq~EMPTY	id~ID~MyIDHandler	msg~MSG	created_on~TIMESTAMP	created_by~STATIC~Thomas Fenner (t.fenner@klopotek.de)
  
  // 6. Layout-options to define the layout of the messages (optional)
  log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
  log4j.appender.JDBC.layout.ConversionPattern=%m
  
  // 7. Buffer-option to define the size of the message-event-buffer (optional)
  log4j.appender.JDBC.buffer=1
  
  // 8. Commit-option to define a auto-commitment
  log4j.appender.JDBC.commit=Y
  */
  
  // Here is a code example to configure the JDBCAppender with a configuration-file :
  
  import org.apache.log4j.*;
  import java.sql.*;
  import java.lang.*;
  import java.util.*;
  
  public class Log4JTest
  {
  	// Create a category instance for this class
     static Category cat = Category.getInstance(Log4JTest.class.getName());
  
     public static void main(String[] args)
     {
        // Ensure to have all necessary drivers installed !
     	try
        {
  			Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
  			DriverManager.registerDriver(d);
  		}
        catch(Exception e){}
  
        // Set the priority which messages have to be logged
  		cat.setPriority(Priority.INFO);
  
  		// Configuration with configuration-file
  		PropertyConfigurator.configure("log4jtestprops.txt");
  
        // These messages with Priority >= setted priority will be logged to the database.
    		cat.debug("debug");  //this not, because Priority DEBUG is less than INFO
        cat.info("info");
        cat.error("error");
        cat.fatal("fatal");
     }
  }
  
  // Here is a code example to configure the JDBCAppender without a configuration-file :
  /*
  import org.apache.log4j.*;
  import java.sql.*;
  import java.lang.*;
  import java.util.*;
  
  public class Log4JTest
  {
  	// Create a category instance for this class
     static Category cat = Category.getInstance(Log4JTest.class.getName());
  
     public static void main(String[] args)
     {
     	// A JDBCIDHandler
  	   MyIDHandler idhandler = new MyIDHandler();
  
        // Ensure to have all necessary drivers installed !
     	try
        {
  			Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
  			DriverManager.registerDriver(d);
  		}
        catch(Exception e){}
  
        // Set the priority which messages have to be logged
  		cat.setPriority(Priority.DEBUG);
  
        // Create a new instance of JDBCAppender
        JDBCAppender ja = new JDBCAppender();
  
        // Set options with method setOption()
        ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
        ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
        ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
        ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
  
  		ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
  
        // There are two ways to setup the column-descriptions :
        // 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
  		//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY	id~ID~MyIDHandler	msg~MSG	created_on~TIMESTAMP	created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
  		// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
  		ja.setLogType("id_seq", LogType.EMPTY, "");
  		ja.setLogType("id", LogType.ID, idhandler);
  		ja.setLogType("msg", LogType.MSG, "");
  		ja.setLogType("created_on", LogType.TIMESTAMP, "");
  		ja.setLogType("created_by", LogType.STATIC, "FEN");
  
        // If you just want to perform a static sql-statement, forget about the table- and columns-options,
        // and use this one :
  		//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
  
        // other options
  		//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
  		//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
  
        // Define a layout
  		//ja.setLayout(new PatternLayout("%m"));
  
        // Add the appender to a category
        cat.addAppender(ja);
  
        // These messages with Priority >= setted priority will be logged to the database.
  		cat.debug("debug");
        cat.info("info");
        cat.error("error");
        cat.fatal("fatal");
  	}
  }
  */
  
  // Implement a sample JDBCConnectionHandler
  class MyConnectionHandler implements JDBCConnectionHandler
  {
  	Connection con = null;
     //Default connection
  	String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
     String username = "mex_pr_dev60";
     String password = "mex_pr_dev60";
  
     public Connection getConnection()
     {
     	return getConnection(url, username, password);
     }
  
     public Connection getConnection(String _url, String _username, String _password)
     {
     	try
        {
     		if(con != null && !con.isClosed()) con.close();
  			con = DriverManager.getConnection(_url, _username, _password);
  			con.setAutoCommit(false);
        }
        catch(Exception e){}
  
     	return con;
     }
  }
  
  
  // Implement a sample JDBCIDHandler
  class MyIDHandler implements JDBCIDHandler
  {
  	private static long id = 0;
  
  	public synchronized Object getID()
     {
  		return new Long(++id);
     }
  }
  
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/code_example1.java
  
  Index: code_example1.java
  ===================================================================
  
  // Here is a code example to configure the JDBCAppender with a configuration-file
  
  import org.apache.log4j.*;
  import java.sql.*;
  import java.lang.*;
  import java.util.*;
  
  public class Log4JTest
  {
     // Create a category instance for this class
     static Category cat = Category.getInstance(Log4JTest.class.getName());
  
     public static void main(String[] args)
     {
        // Ensure to have all necessary drivers installed !
        try
        {
           Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
           DriverManager.registerDriver(d);
        }
        catch(Exception e){}
  
        // Set the priority which messages have to be logged
        cat.setPriority(Priority.INFO);
  
        // Configuration with configuration-file
        PropertyConfigurator.configure("log4jtestprops.txt");
  
        // These messages with Priority >= setted priority will be logged to the database.
        cat.debug("debug");  //this not, because Priority DEBUG is less than INFO
        cat.info("info");
        cat.error("error");
        cat.fatal("fatal");
     }
  }
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/code_example2.java
  
  Index: code_example2.java
  ===================================================================
  
  // Here is a code example to configure the JDBCAppender without a configuration-file
  
  import org.apache.log4j.*;
  import java.sql.*;
  import java.lang.*;
  import java.util.*;
  
  public class Log4JTest
  {
  	// Create a category instance for this class
  	static Category cat = Category.getInstance(Log4JTest.class.getName());
  
  	public static void main(String[] args)
  	{
  		// A JDBCIDHandler
  		MyIDHandler idhandler = new MyIDHandler();
  
  		// Ensure to have all necessary drivers installed !
  		try
  		{
  			Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
  			DriverManager.registerDriver(d);
  		}
  		catch(Exception e){}
  
  		// Set the priority which messages have to be logged
  		cat.setPriority(Priority.DEBUG);
  
  		// Create a new instance of JDBCAppender
  		JDBCAppender ja = new JDBCAppender();
  
  		// Set options with method setOption()
  		ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
  		ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
  		ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
  		ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
  
  		ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
  
  		// There are two ways to setup the column-descriptions :
  		// 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
  		//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY	id~ID~MyIDHandler	msg~MSG	created_on~TIMESTAMP	created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
  
  		// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
  		ja.setLogType("id_seq", LogType.EMPTY, "");
  		ja.setLogType("id", LogType.ID, idhandler);
  		ja.setLogType("msg", LogType.MSG, "");
  		ja.setLogType("created_on", LogType.TIMESTAMP, "");
  		ja.setLogType("created_by", LogType.STATIC, "FEN");
  
  		// If you just want to perform a static sql-statement, forget about the table- and columns-options,
  		// and use this one :
  		//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
  
  		// other options
  		//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
  		//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
  
  		// Define a layout
  		//ja.setLayout(new PatternLayout("%m"));
  
  		// Add the appender to a category
  		cat.addAppender(ja);
  
  		// These messages with Priority >= setted priority will be logged to the database.
  		cat.debug("debug");
  		cat.info("info");
  		cat.error("error");
  		cat.fatal("fatal");
  	}
  }
  
  // Implement a sample JDBCConnectionHandler
  class MyConnectionHandler implements JDBCConnectionHandler
  {
  	Connection con = null;
     //Default connection
  	String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
     String username = "mex_pr_dev60";
     String password = "mex_pr_dev60";
  
     public Connection getConnection()
     {
  	return getConnection(url, username, password);
     }
  
     public Connection getConnection(String _url, String _username, String _password)
     {
  	try
  	  {
  		if(con != null && !con.isClosed()) con.close();
  			con = DriverManager.getConnection(_url, _username, _password);
  			con.setAutoCommit(false);
  	  }
  	  catch(Exception e){}
  
  	return con;
     }
  }
  
  
  // Implement a sample JDBCIDHandler
  class MyIDHandler implements JDBCIDHandler
  {
  	private static long id = 0;
  
  	public synchronized Object getID()
     {
  		return new Long(++id);
     }
  }
  
  
  
  
  1.1                  jakarta-log4j/contribs/ThomasFenner/configfile_example.txt
  
  Index: configfile_example.txt
  ===================================================================
  # Here is a Configuration-file example, which can be used with the PropertyConfigurator :
  
  # Declare a appender variable named JDBC
  log4j.rootCategory=JDBC
  
  # JDBC is a class of JDBCAppender, which writes messages into a database
  log4j.appender.JDBC=JDBCAppender
  
  # 1. Database-options to connect to the database
  log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
  log4j.appender.JDBC.username=mex_pr_dev60
  log4j.appender.JDBC.password=mex_pr_dev60
  
  # 2. Connector-option to specify your own JDBCConnectionHandler
  #log4j.appender.JDBC.connector=MyConnectionHandler
  
  # 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
  #log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
  
  # 4. Table-option to specify one table contained by the database
  log4j.appender.JDBC.table=logtest
  
  # 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
  log4j.appender.JDBC.columns=id_seq~EMPTY	id~ID~MyIDHandler	msg~MSG	created_on~TIMESTAMP	created_by~STATIC~Thomas Fenner (t.fenner@klopotek.de)
  
  # 6. Layout-options to define the layout of the messages (optional)
  #log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
  #log4j.appender.JDBC.layout.ConversionPattern=%m
  
  # 7. Buffer-option to define the size of the message-event-buffer (optional)
  #log4j.appender.JDBC.buffer=1
  
  # 8. Commit-option to define a auto-commitment (optional)
  #log4j.appender.JDBC.commit=Y