Its actually a combination of the code in TCLFilter and the org.jboss.web.tomcat.service.WebCtxLoader's ENCLoader which is causing this issue. The TCLFilter has this piece of code
/** Start with the current thread context class loader
* @return true if the caller tcl has a url matching our deployURL
*/
private boolean isMatchingTCL()
{
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
if( matchSet.contains(tcl) )
return true;
if( missSet.contains(tcl) )
return false;
// Search the class loader URLs for a match
ClassLoader cl = tcl;
boolean match = false;
while( cl != null )
{
URL[] urls = getClassLoaderURLs(cl);
for(int n = 0; n < urls.length; n ++)
{
URL u = urls[n];
String file = u.getFile();
if( file.indexOf(deployURL) > 0 )
And relies on this getClassLoaderURLs method (which relies on reflection) to see whether the current classloader corresponds to the deployURL:
/** Use reflection to access a URL[] getURLs method so that non-URLClassLoader
class loaders that support this method can provide info.
*/
private static URL[] getClassLoaderURLs(ClassLoader cl)
{
URL[] urls = {};
try
{
Class returnType = urls.getClass();
Class[] parameterTypes = {};
Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
if( urls == null || urls.length == 0 )
{
getURLs = cl.getClass().getMethod("getClasspath", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
}
}
catch(Exception ignore)
{
}
return urls;
}
The .war application (ex: jmx-console.war) is loaded through the WebCtxLoader's ENCLoader. The ENCLoader has package static access:
public class WebCtxLoader
implements Lifecycle, Loader
{
private static final Logger log = Logger.getLogger(WebCtxLoader.class);
/**
* The ClassLoader used to scope the ENC
*/
protected ClassLoader encLoader;
... // removed irrelevant code intentionally
... // removed irrelevant code intentionally
... // removed irrelevant code intentionally
// This won't be accessible to the TCLFilter
/**
* A trival extension of URLClassLoader that uses an empty URL[] as its
* classpath so that all work is delegated to its parent.
*/
static class ENCLoader extends URLClassLoader
{
... // removed irrelevant code intentionally
public URL[] getURLs()
{
return urllist;
}
}
}
and hence is not available through reflection to the TCLFilter. As a result, the filter always has a empty set of URLs and hence the filtering fails.
Looking at the code in the TCLFilter and the ENCLoader (which extends URLClassloader), i guess the TCLFilter's getClassLoaderURLs(ClassLoader cl) method can be patched to do this:
/**
* First check if the cl is of type URLClassloader. If yes, then use the getURLs API without the
* need for reflection. If its not a URLClassloader type then go for reflection
* to access a URL[] getURLs method so that non-URLClassLoader
* class loaders that support this method can provide info.
*/
private static URL[] getClassLoaderURLs(ClassLoader cl)
{
URL[] urls = {};
try
{
// FIrst check the type of cl, if URLClassloader type then don't use reflection
if (cl instance of URLClassLoader)
{
URLClassLoader urlClassLoader = (URLClassLoader) cl;
urls = urlClassLoader.getURLs();
}
else
{ //let's rely on reflection
Class returnType = urls.getClass();
Class[] parameterTypes = {};
Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
if( urls == null || urls.length == 0 )
{
getURLs = cl.getClass().getMethod("getClasspath", parameterTypes);
if( returnType.isAssignableFrom(getURLs.getReturnType()) )
{
Object[] args = {};
urls = (URL[]) getURLs.invoke(cl, args);
}
}
}
}
catch(Exception ignore)
{
}
return urls;
}
That should fix the problem. Then your log4j.xml can have this:
<appender name="App1Log" class="org.apache.log4j.FileAppender">
....
<filter class="org.jboss.logging.filter.TCLFilter">
<param name="AcceptOnMatch" value="true"/>
<param name="DeployURL" value="jmx-console.war"/>
</filter>
<!-- end the filter chain here -->
<filter class="org.apache.log4j.varia.DenyAllFilter"></filter>
</appender>