Can't override java.util.logging.LogManager in a Spring Boot web application: Getting java.lang.ClassNotFoundException on already loaded class - spring-boot

I am trying to override java.util.logging.LogManager with my own configuration:
class CloudwatchHandler is an implementation of Handler and includes this init() method:
public static void init() {
final String julConfigFile = System.getProperty("java.util.logging.config.file");
if(julConfigFile != null) {
try (InputStream is = new FileInputStream(julConfigFile)) {
LogManager logManager = LogManager.getLogManager();
logManager.reset();
logManager.readConfiguration(is);
Logger logger = Logger.getLogger(CloudwatchHandler.class.getName());
logger.info("LOADED");
} catch (SecurityException | IOException e) {
System.err.println(Instant.now() + ": Failed to initialize JUL.");
e.printStackTrace(System.err);
throw new RuntimeException(e);
}
}
else {
System.err.println(Instant.now() + ": java.util.logging.config.file was not specified");
}
}
Application main class
public static void main(String[] args) {
CloudwatchHandler.init();
SpringApplication.run(MyApp.class, args);
}
Error
Can't load log handler "mypackage.CloudwatchHandler"
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at java.logging/java.util.logging.LogManager.createLoggerHandlers(LogManager.java:1005)
at java.logging/java.util.logging.LogManager$4.run(LogManager.java:975)
at java.logging/java.util.logging.LogManager$4.run(LogManager.java:971)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
at java.logging/java.util.logging.LogManager.loadLoggerHandlers(LogManager.java:971)
at java.logging/java.util.logging.LogManager.initializeGlobalHandlers(LogManager.java:2424)
at java.logging/java.util.logging.LogManager$RootLogger.accessCheckedHandlers(LogManager.java:2526)
at java.logging/java.util.logging.Logger.getHandlers(Logger.java:2090)
at java.logging/java.util.logging.Logger.log(Logger.java:977)
at java.logging/java.util.logging.Logger.doLog(Logger.java:1007)
at java.logging/java.util.logging.Logger.log(Logger.java:1030)
at java.logging/java.util.logging.Logger.info(Logger.java:1803)
at mypackage.CloudwatchHandler.init(CloudwatchHandler.java:51)
... main ...
The really crazy thing about this exception is that the class causing the ClassNotFoundException is actually a caller in the current stack frame, as seen in the stack trace. So clearly it has been FOUND or it couldn't be running.
What's causing this and how can I fix it? I just want to load my own log handler.
Spring Boot version is 2.6.3.

ClassNotFoundException can occur if the Handler is not deployed to load in the system class loader as that is what the LogManager uses to find handlers.
Update your test case and try again:
public static void main(String[] args) throws Exception {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader());
System.out.println(CloudwatchHandler.class.getClassLoader());
//This is what CloudwatchHandler.init(); triggers
Class.forName(CloudwatchHandler.class.getName(), true, Thread.currentThread().getContextClassLoader());
//This is what the LogManager is doing
Class.forName(CloudwatchHandler.class.getName(), true, ClassLoader.getSystemClassLoader());
//Force load the root handlers.
Logger.getLogger("").getHandlers();
CloudwatchHandler.init();
SpringApplication.run(MyApp.class, args);
}
If it is that the handler is deployed in the context class loader and not in the system classloader then you need to change how you package the handler so it is visible to the system classloader. The java.util.logging.config.class option is one part of the LogManager that will try loading classes via context classloader which is what will be able to see your classes. For this option you move the contents of your init method to a new class and have the constructor perform the action. On the command line then set the value to the FQCN of the new config class.

Related

AWS Lambda timeout issue with spring boot application

I have a spring boot application which I am trying to deploy on AWS lambda .
I added StreamLambdaHandler as the handler class
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
//handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(SituationalFlexibilityApp.class);
// For applications that take longer than 10 seconds to start, use the async builder:
handler = new SpringBootProxyHandlerBuilder<AwsProxyRequest>()
.defaultProxy()
.asyncInit()
.springBootApplication(SituationalFlexibilityApp.class)
.buildAndInitialize();
// we use the onStartup method of the handler to register our custom filter
handler.onStartup(servletContext -> {
FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter",CognitoIdentityFilter.class);
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
});
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
public StreamLambdaHandler() {
Timer.enable();
}
/*
* public StreamLambdaHandler() throws ContainerInitializationException {
*
* handler = new SpringBootProxyHandlerBuilder() .defaultProxy() .asyncInit()
* .springBootApplication(SlowApplication.class) .buildAndInitialize(); }
*/
#Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
handler.proxyStream(input, output, context);
}
When I test it on AWS lambda I get below exception
com.amazonaws.serverless.exceptions.ContainerInitializationException: Could not initialize framework within the 20000ms timeout
so I updated the lambda configuration for a timeout of 5 mins and added below line in the static block of the StreamLambdaHandler class
LambdaContainerHandler.getContainerConfig().setInitializationTimeout(2000000);
Now, I am seeing below exception
Exception in thread "Thread-0" java.lang.IllegalArgumentException: Could not find timer SPRINGBOOT2_COLD_START
Can someone please point me in the correct direction as I a noob in AWS services and lambda
I am not seeing this error after commenting out the below code in StreamLambdaHandler method
// Timer.enable();

Why doesn't my Spring-based web application respond to its defined endpoints

I'm developing a simple Spring-based web service userSetting to run on Liberty 8.5.5.9. I'm able to build and deploy the service, and when I start it, it appears to understand its context root (userSetting) but none of its endpoints; I believe I'm missing some essential Spring-based linkage of the parts of the application, but can't figure out which.
The main class of the application looks like:
#SpringBootApplication
#ImportResource("classpath:spring-resource1.xml")
#Import({RESTInterface.class})
public class UserSettingApplication {
private static final LoggerUtils logger = new LoggerUtils( UserSettingApplication.class );
public static void main(String[] args) {
logger.debug( "Entering UserSettingApplication.main()" );
System.out.println( "Entering UserSettingApplication.main()" );
SpringApplication.run(UserSettingApplication.class, args);
logger.debug( "Exiting UserSettingApplication.main()" );
System.out.println( "Exiting UserSettingApplication.main()" );
}
// Dan Vega says: Remember that this going to execute after the
// application context is loaded so you could use it to check if
// certain beans exist or what values of certain properties are.
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
logger.info("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
logger.info(beanName);
}
};
}
}
and the class that defines the endpoints (abbreviated to a single end point) is:
#CrossOrigin
#RestController
#RequestMapping(path = "userSetting")
public class RESTInterface {
...
#RequestMapping(path = "/hello", method = { RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String hello() {
return "Hello, dude!";
}
}
when I try to hit the endpoint http://localhost:9080/userSetting/userSetting/hello, I get the message:
Error 404: java.io.FileNotFoundException: SRVE0190E: File not found: /userSetting/hello
It appears that the RestController has not gotten properly hooked up to the application; in desperation, I tried adding the following line to the main class:
#Import({RESTInterface.class})
but this didn't change anything. Can someone please tell me how to properly connect in the endpoints?
Try hitting http://localhost:9080/userSetting/hello
You are giving 'userSetting' twice in the url

NoClassDefFoundError in Osgi environment

I am working with osgi on apache karaf and I am trying to use kafka and debezium to run into an osgi environment.
kafka and debezium were not osgi ready (karaf will not consider them as bundles), so I did osgified them using eclipse "Plug-in project". The jars that I osgified them are the following : debezium-embedded, debezium-core, kafka connect-api, kafka connect-runtime.
At the begining I get alot of "Class not found exception" when I try to run debezium..
In order to resolve this problem, I changed the manifest of the two bundles. I added an import package to the caller and an export package to the called bundle. Using this I can solve the classNotFound issue.
After solving all the classNotfound issues, I get NoClassDefFoundError
NoClassDefFoundError means that the class loader could not find the .class when it tries to load them ... But I did import all the packages and export them as well.
Any thoughts how to deal with NoClassDefFoundError in an osgi environement
[EDIT Added code]
This is the class Monitor :
public class Monitor {
private Consumer<SourceRecord> consumer = new Consumer<SourceRecord>() {
public void accept(SourceRecord t) {
System.out.println("Change Detected !");
}
};
public void connect() {
System.out.println("Engine Starting");
Configuration config = Configuration.create()
/* begin engine properties */
.with("connector.class", "io.debezium.connector.mysql.MySqlConnector")
.with("offset.storage", "org.apache.kafka.connect.storage.FileOffsetBackingStore")
.with("offset.storage.file.filename", "d:/pathTooffset.dat")
.with("offset.flush.interval.ms", 60000)
/* begin connector properties */
.with("name", "my-sql-connector").with("database.hostname", "localhost").with("database.port", 3306)
.with("database.user", "root").with("database.password", "apassword").with("server.id", 10692)
.with("database.server.name", "localhost")
.with("database.history", "io.debezium.relational.history.FileDatabaseHistory")
.with("database.history.file.filename", "d:/pathTOdbhistory.dat")
.build();
try {
// Create the engine with this configuration ...
EmbeddedEngine engine = EmbeddedEngine.create().using(config).notifying(consumer).build();
Executor executor = Executors.newFixedThreadPool(1);
executor.execute(() -> {
engine.run();
});
} catch (Exception e) {
e.printStackTrace();
}
}
And my activator :
public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
Monitor monitor = new Monitor();
monitor.connect();
}
public void stop(BundleContext context) throws Exception {
}}
The problem must be inside EmbeddedEngine. The error could not initialize class means that some static initialization of the class did not work. See this related question noclassdeffounderror-could-not-initialize-class-error.
I propose to run karaf in debug mode and debug through the initialization of this class.

Calling Spring Main Method from Another Class Error

I am working on a Plugin for an OpenFire Server. I am trying to integrate Spring into this plugin. When the plugin is initialized, I will like to call the Main Method for my Spring.
When I execute the Spring alone, it works fine, but when I call its main method from my plugin, I get an exception.
How am I suppose to call the Spring Main Method.
What am I missing. Any help will be appreciated. Thank you.
Spring Main Class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
try {
SpringApplication.run(Application.class, args);
System.out.println("No error");
} catch (Exception e) {
System.out.println("Error " + e);
}
}
}
OpenFire Plugin:
public class FetchNewsPlugin implements Plugin {
#Override
public void initializePlugin(PluginManager manager, File pluginDirectory) {
Runnable r = new Runnable() {
public void run() {
String[] args = {};
Application.main(args);
}
};
new Thread(r).start();
System.out.println("Plugin Intitialized");
}
#Override
public void destroyPlugin() {
}
}
Log Output:
Exception in thread "Thread-13" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at hello.Application.main(Application.java:11)
at org.clinton.openfire.plugin.FetchNewsPlugin$1.run(FetchNewsPlugin.java:20)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 3 more
Where Exception is being thrown:
/**
* Finds and loads the class with the specified name from the URL search
* path. Any URLs referring to JAR files are loaded and opened as needed
* until the class is found.
*
* #param name the name of the class
* #return the resulting class
* #exception ClassNotFoundException if the class could not be found,
* or if the loader is closed.
* #exception NullPointerException if {#code name} is {#code null}.
*/
protected Class findClass(final String name)
throws ClassNotFoundException
{
final Class result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction>() {
public Class run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/bin/build/lib/ant/slf4j-simple.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/bin/build/lib/dist/slf4j-log4j12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/build/lib/ant/slf4j-simple.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/clinton/git/Openfire/build/lib/dist/slf4j-log4j12.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
It seems like you are using URLClassLoader to load classes that your needed. However, an URLClassLoader should contain all items or jars it depends on not only the jar itself.
In other words, you'd better extract your jar and add all necessary items to your URLClassLoader.
In this way it should work. And please let me know if any problem.
And question https://stackoverflow.com/a/37339725/5619827 could be helpful.
Thanks #Gemini for helping, but I guess I was too dumb to follow his way. I found another workaround: Since the Spring could start successfully when executed alone. I bundled the Spring into a runnable jar file and executed using the following when th plugin starts:
private void startSpring() {
try {
ProcessBuilder processBuilder = new ProcessBuilder("/usr/lib/jvm/java-8-oracle/bin/java", "-jar",
"/home/clinton/git/Maven/target/mavenproject2-1.0-SNAPSHOT.jar");
processBuilder.directory(new File("/home/clinton/git/Maven/Working"));
File log = new File("log");
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(Redirect.appendTo(log));
Process p = processBuilder.start();
assert processBuilder.redirectInput() == Redirect.PIPE;
assert processBuilder.redirectOutput().file() == log;
assert p.getInputStream().read() == -1;
System.out.println("Started success");
//Process p = processBuilder.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I hope this helps someone too. I always find a way :)

Remote JNDI access to a single resource in TomEE

I'm trying to set an object into JNDI and then get remote access to it. I'm using TomEE 1.6.0. I'm setting a sinple string using an servlet like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException
{
try
{
Context ctx=new InitialContext();
ctx.bind("myKey","MY STRING");
}
catch(NamingException ex)
{
ex.printStackTrace();
}
}
After running and invoke this servlet, I try to get remote access through JNDI using this standalone main.
public static void main(String[] args) throws Exception
{
Context ctx = getContext();
String nom = (String)ctx.lookup("java:/comp/env/nombre");
System.out.println(nom);
}
private static Context getContext() throws Exception
{
Hashtable<String,String> t = new Hashtable<>();
t.put("java.naming.factory.initial","org.apache.openejb.client.RemoteInitialContextFactory");
t.put("java.naming.provider.url","http://127.0.0.1:8080/tomee/ejb");
return new InitialContext(t);
}
But it throws an NameNotFoundException like this:
Exception in thread "main" javax.naming.NameNotFoundException: /comp/env/nombre does not exist in the system. Check that the app was successfully deployed.
at org.apache.openejb.client.JNDIContext.lookup(JNDIContext.java:319)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at demo.TestJNDI.main(TestJNDI.java:13)
So, my question are two:
1 - How can I know the default JNDI name which is using TomEE to publish this string?
2 - How can I set this string into any XML file instead the servlet?
Thanks!
Not sure what you expect to do but remote context is mainly an ejb/resource one. comp/env is clearly local to the application

Resources