Ignatia sent me a question about classloaders (at the end of this), but 
after writing a long response I thought it was worth mailing to the list 
too so folk can comment/critique. The issue is the common one of not 
getting log4j to initialise properly after creating your own 
log4j.properties and removing the one in axis.jar.

Ignatia, it seems you must have your log4j.jar and log4j.properties 
being loaded by different classloaders. I suspect your axis.jar is in 
tomcat's common/lib folder. The rest of this email explains whats going 
on and what the possible setups are.

There are two issues involved here: the arcane rules of classloaders and 
  which class references what. The explanation below is for tomcat 4 (ie 
servlet 2.3 spec) - tomcat 3 is a servlet 2.2 implementation and 
classloader order is DIFFERENT. Its also my understanding which may only 
apply in a parallel universe...

A typical jaxrpc usage begins with
sf=javax.xml.rpc.ServiceFactory.newInstance();
this in turn loads an axis implementation of the service[2]. Axis then 
invokes commons-logging to set up its logging mechansim. commons-logging 
then invokes log4j if installed, and finally log4j's basicconfigurator 
loads log4j.properties. So the loading sequence is:

jaxrpc -> axis -> commons-logging -> log4j -> log4j.properties

Now look at 
http://jakarta.apache.org/tomcat/tomcat-4.0-doc/class-loader-howto.html, 
especially this diagram showing how the classloaders fit together:

       Bootstrap
           |
        System
           |
        Common
       /      \
  Catalina   Shared
              /   \
         Webapp1  Webapp2 ...

if jaxrpc is loaded by the 'common' classloader, it can only see up the 
tree to 'System' and 'Bootstrap'. This means if you install jaxrpc in 
the 'common/lib' folder in tomcat, you MUST install axis.jar[1], 
commons-logging.jar, log4j.jar in there too for them to be found. 
log4j.properties must be in one of these jars or in 'common/classes' to 
be found.

If you place commons-logging, log4j, and log4j.properties in your webapp 
(as well as placing everything in 'common' as above), you will get 
logging working, but in a way that may surprise you. While axis now gets 
the common log4j, your webapp gets its own instance of log4j. This means 
you can change where your webapp logs to, but not the axis parts of it! 
Axis will keep on using the common log4j.jar.

Things get even more confused if you set your system classpath to 
contain jaxrpc (rather than putting it in the common directory). Again 
you have the same 'sucking' effect where you end up having to put 
log4j.properties in the same classpath as jaxrpc. However, now you'll 
find you can't override the log4j.properties at all on a per-webapp 
basis. The reason is in tomcat's javadoc for WebappClassLoader: "The 
system class loader will be queried first, then the local repositories, 
and only then delegation to the parent class loader will occur. This 
allows the web application to override any shared class except the 
classes from J2SE"

The way I read this, if log4j.properties(A) was in the system classpath 
and log4j.properties(B) was in WEB-INF/classes, then (A) would be read 
FIRST. If log4j.jar was in the system classpath, and log4j.properties is 
only in the webapp, then it /wont load at all/ as now log4j can no 
longer 'see' the webapp's classpath.

Summing up so far, there are three possible working setups where your 
webapp uses commons-logging.

COMMON LOGGING: every webapp gets the same log
common/lib/jaxrpc.jar
common/lib/axis.jar #etc
common/lib/commons-logging.jar
common/lib/log4j.jar
common/classes/log4j.properties

COMMON AXIS LOGGING: same axis log, separate webapp logs
common/lib/jaxrpc.jar
common/lib/axis.jar #etc
common/lib/commons-logging.jar
common/lib/log4j.jar
common/classes/log4j.properties
webapps/app/WEB-INF/lib/commons-logging.jar
webapps/app/WEB-INF/lib/log4j.jar
webapps/app/WEB-INF/classes/log4j.properties

SEPARATE LOGGING: suck it and see
webapps/app/WEB-INF/lib/jaxrpc.jar
webapps/app/WEB-INF/lib/axis.jar #etc
webapps/app/WEB-INF/lib/commons-logging.jar
webapps/app/WEB-INF/lib/log4j.jar
webapps/app/WEB-INF/classes/log4j.properties

this last one is interesting. Tomcat's WebappClassloader uses a 
different delegation model for 'javax.*' - these classes are searched 
for in the parent classloader first, then in the webapps own repository. 
The upshot of this is that a shared jaxrpc will ALWAYS be found first, 
and if it is found, separate logging WILL FAIL. That's why I've not 
listed any classes in common/* for this solution.

There is still one stumbling block in the way of having separate logging 
for axis on a per-webapp basis, and its this line in happyaxis.jsp:
"On Tomcat 4.x, you may need to put libraries that contain java.* or 
javax.* packages into CATALINA_HOME/commons/lib", see also 
http://xml.apache.org/axis/faq#faq5

This jars a bit with my understanding. Tomcat won't let me override 
anything in the system classpath (ie anything in the jdk) using a simple 
  mechanism - it checks in the system classloader first for everything. 
The only other filter for "javax" stuff ensures that the parent 
classloaders are searched first, as described above. This means that if 
you have no jaxrpc.jar in your jdk or in tomcat's common area, you can 
use a different jaxrpc for each webapp. Which means (finally) that you 
should be able to have separate axis logs for each webapp.

I should emphasise that I've not tried all of this - my setup already 
was the 'common logging' one before I started using axis.

Anyway. Anyone care to poke holes in the logic? Does anyone actually 
have separate axis logs for different webapps working?


-Baz

[1] I'm using axis.jar as shorthand for all the rest of the axis jars 
here too.
[2] if you invoke axis directly as you did before jaxrpc appeared, then 
you lose portablility, but none of the restrictions on the placement of 
jaxrpc.jar mentioned at the end of the article apply.

Ignatia Suwarna wrote:
> Hi Brian,
> 
> I read your email about Log4j from Axis mailing list.
> I am having similar trouble and would like to ask for your suggestions.
> 
> I changed log4j.properties from axis.jar:
> # Set root category priority to ERROR and its only appender to A1.
> log4j.rootCategory=INFO, LOGFILE
> log4j.category.org.apache.axis=INFO, LOGFILE
> #log4j.rootCategory=DEBUG, CONSOLE, LOGFILE
> 
> # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
> log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
> log4j.appender.CONSOLE.Threshold=INFO
> log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
> log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
> 
> # LOGFILE is set to be a File appender using a PatternLayout.
> log4j.appender.LOGFILE=org.apache.log4j.FileAppender
> 
>log4j.appender.LOGFILE.File=C:\Netscape\Servers\bin\https\webapps\axis\WEB-INF\classes\axis.log
> log4j.appender.LOGFILE.Append=true
> log4j.appender.LOGFILE.Threshold=INFO
> log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
> log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x -
> %m%n
> 
> I also set up the path in my servlet to point to lo4j.properties in
> \WEB-INF\classes directory.  I have removed log4j.properties from
> axis.jar.
> 
> I am getting this error:
> log4j:ERROR No appenders could be found for category
> (org.apache.axis.utils.Opti
> ons).
> log4j:ERROR Please initialize the log4j system properly.
> AxisFault
> 
> Do you know how to resolve this? Am I missing something else?
> 
> I would really appreciate any suggestions/help.
> 
> Thank you very much.
> 
> Ignatia Suwarna
> 
> 
> 


Reply via email to