Trying to load log4j 1.2 in Lucee 5.3.9.108-RC2

I’m hoping someone can help me with this Lucee classloader issue I’m having, related to trying to get Log4j 1.2 running in Lucee 5.3.9.108-RC2.

I’ve dropped the log4j-1.2.17.jar into a folder that is loaded by the JVM via the classpath. I can run createObject("java", "org.apache.log4j.Logger") and Lucee will load the JAR.

Now I have another JAR (let’s call it Example.jar) being loaded via this.javaSettings in the Application.cfc and I have the loadColdFusionClassPath property set to true . I can call createObject() to load classes in Example.jar and it an instance of a class is created.

However, the Example.jar is not able to see the log4j-1.2.17.jar classes. Any attempts to initiate classes that rely on the Log4j v1 return a huge stacktrace, the root appears to be with a class loader issue:

Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 137 more

If I move Example.jar into the same folder as log4j-1.2.17.jar then everything works as expected. However, I’m trying to keep Example.jar in a path loaded by this.javaSettings because it’s updated frequently and I want to be able to dynamically reload the JAR.

Neither of the Example.jar nor log4j-1.2.17.jar are part of an OSGi bundle, so I don’t think it’s related to that.

Why would using Lucee’s JavaLoader prevent the class from being found? Should this be working? Am I misunderstanding something?

You’d be able to do that more easily if you loaded Example.jar as an OSGi bundle, if that’s possible.

Unfortunately, it’s not an OSGi bundle. We’re relying on a number of third party libraries that use Log4j 1.x, while I suppose I could try to rebundle them all for Lucee, we also need to maintain compatibility with older ACF for the immediate future (as we’ve got to maintain both platforms right now). I can’t wait to get off of ACF completely as it will make things much easier.

JavaLoader? It’s old but it does all the things: isolation, dynamic reloading, cross-engine…

1 Like

I might have to go that route.

What happens if you pull log4j out of the server classpath and put it with the application javasettings?

I’d worry less about reusing log4j from further up and just keep all the dependencies together in the app - since the app based classloader should already be isolated, osgi or not.

You probably know a lot more about this than me, Joe, but my limited understanding is that it’s not.

I tried putting it in the javaSettings, but that caused other classloader issues where it complained about finding multiple interfaces.

Ultimately, the only reliable solution I found was just to go back to using JavaLoader.cfc for all my Java libs that rely on Log4j.

Hopefully once I’m completely off of ACF and on Lucee I can just use OSGi across the board for all my external Java dependencies to avoid the classloader issues.

I have created a jar that simply instantiate a class from Log4j 1 and does return it (see attachment)

  1. add to the main classpath
    I added my jar and the log4j1 jar to “/path/to/lucee/lib/ext” and restarted Lucee (this is essential, classpath is only read on startup)
    then i did successfull execute it like this:
    dump(createObject("java","org.lucee.test.Log4j1").get());

  2. custom classpath folder:
    I added my jar and the log4j1 jar to a folder not touched by lucee “/path/to/lucee/customlib”, then i did successfull execute it like this:

dump(createObject("java","org.lucee.test.Log4j1","/path/to/lucee/customlib").get()); // pointing to folder
dump(createObject("java","org.lucee.test.Log4j1",
    ["/path/to/lucee/customlib/log4j-1.2.17.jar","/path/to/lucee/customlib/log4j-test.jar"]).get()); // pointing to files
  1. use this.javasettings
    I added my jar and the log4j1 jar to a folder not touched by lucee “/customlib”, then i added the following to the Application.cfc
	this.javaSettings = {
        loadPaths: ["/path/to/lucee/customlib"]
    };

after that i could successfully execute this
dump(createObject("java","org.lucee.test.Log4j1").get());

same for this setting in application.cfc

	this.javaSettings = {
        loadPaths: ["/path/to/lucee/customlib/log4j-test.jar","/path/to/lucee/customlib/log4j-1.2.17.jar"]
    };

We do not have anything in place that is preventing log4j 1 to get loaded as a regular jar.
So please give my code examples a try with my jar and yours.

I think the problem is in how your jar is loading the log4j jar, your stacktrace indicate that a URLClassloader is used, Lucee normally does not use them, i assume your jar simply looks in the wrong place.
Can you please provide the full stacktrace you get.

log4j-test.jar (608 Bytes)

Thanks for taking the time to put together some code.

Here’s an example of class I’m using:

package com.test;

import org.apache.log4j.*;
import java.util.Properties;
import java.io.*;

public class TestClassLogger {
	public static void configure(){
		/*
		 * Initialize the Logger with the default configuration
		 * we want to use for logging these events.
		 * 
		 * NOTE - The following does not appear to correctly load the property files:
		 * 
		 * PropertyConfigurator.configure("resources/log4j.properties");
		 * 
		 * So, we do things manually by loading the resource file as a steam, then loading
		 * it as a new properties configuration object.
		 */
		Properties props = new Properties();
		InputStream is = TestClassLogger.class.getClassLoader().getResourceAsStream("resources/com.giva.clamavclient/log4j.properties");

		try {
			// load the properties
			props.load(is);
		} catch( Exception e ){
			// ignore this exception
		} finally {
			try {
				// close the resource file
				is.close();
			} catch( Exception e ){
				// ignore this exception
			}
		}

		PropertyConfigurator.configure(props);
	}

	public static Logger getLogger(){
		// configure the logger
		configure();

		return (Logger) Logger.getLogger(TestClassLogger.class.getName());
	}

}

The problem is in the getLogger() method.

The problem when I load the log4j v1 JAR in either javaSettings or via createObject() is I get a classloader exception that it finds multiple interfaces in the log4j namespace (I can’t recall the specific error).