NoClassDefFoundError in Osgi environment - osgi

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.

Related

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

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.

How can I shutdown Spring boot thread pool project amicably which is 24x7 running

I have created spring boot thread pool project which has thread that needs to run 24x7 once spawned but when I need to stop the app in server for some maintenance it should shutdown after completing its current task and not taking up any new task.
My code for the same is:
Config class
#Configuration
public class ThreadConfig {
#Bean
public ThreadPoolTaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executorPool = new ThreadPoolTaskExecutor();
executorPool.setCorePoolSize(10);
executorPool.setMaxPoolSize(20);
executorPool.setQueueCapacity(10);
executorPool.setWaitForTasksToCompleteOnShutdown(true);
executorPool.setAwaitTerminationSeconds(60);
executorPool.initialize();
return executorPool;
}
}
Runnable class
#Component
#Scope("prototype")
public class DataMigration implements Runnable {
String name;
private boolean run=true;
public DataMigration(String name) {
this.name = name;
}
#Override
public void run() {
while(run){
System.out.println(Thread.currentThread().getName()+" Start Thread = "+name);
processCommand();
System.out.println(Thread.currentThread().getName()+" End Thread = "+name);
if(Thread.currentThread().isInterrupted()){
System.out.println("Thread Is Interrupted");
break;
}
}
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void shutdown(){
this.run = false;
}
}
Main class:
#SpringBootApplication
public class DataMigrationPocApplication implements CommandLineRunner{
#Autowired
private ThreadPoolTaskExecutor taskExecutor;
public static void main(String[] args) {
SpringApplication.run(DataMigrationPocApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
for(int i = 1; i<=20 ; i++){
taskExecutor.execute(new DataMigration("Task " + i));
}
for (;;) {
int count = taskExecutor.getActiveCount();
System.out.println("Active Threads : " + count);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 0) {
taskExecutor.shutdown();
break;
}
}
System.out.println("Finished all threads");
}
}
I need help to understand if I need to stop my spring boot application it should stop all the 20 threads running which runs (24x7) otherwise after completing there current loop in while loop and exit.
I would propose couple of changes in this code to resolve the problem
1) since in your POC processCommand calls Thread.sleep, when you shutdown the executor and it interrupts workers InterruptedException get called but is almost ignored in your code. After that there is if(Thread.currentThread().isInterrupted()) check which will return false for the reason above. Similar problem is outlined in the post below
how does thread.interrupt() sets the flag?
the following code change should fix the problem:
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
shutdown();
}
}
2) Also because of ThreadConfig::taskExecutor executorPool.setWaitForTasksToCompleteOnShutdown(true) Spring will call executor.shutdown instead of executor.shutdownNow. According to javadoc ExecutorService.shutdown
Initiates an orderly shutdown in which previously submitted tasks are
executed, but no new tasks will be accepted.
So I would recommend to set
executorPool.setWaitForTasksToCompleteOnShutdown(false);
Other things to improve in this code: although DataMigration is annotated as a component the instances of this class are creared not by Spring. You should try using factory method similar to ThreadConfig::taskExecutor in order to make Spring initiate instances of DataMigration for example to inject other bean into DataMigration instances.
In order to shutdown executor when running jar file on linux environment you can for example add actuator module and enable shutdown endpoint:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
in application.properties:
endpoints.shutdown.enabled=true
It will enable JMX shutdown endpoint and you can call shutdown on it.
If you want current job cycle of the task to be finished you should set
executorPool.setWaitForTasksToCompleteOnShutdown(true);
In order to connect to your jvm process on linux env remotely you have to specify an RMI Registry port.
Here is a detailed article:
How to access Spring-boot JMX remotely
If you just need to connect to JMX from local env you can run jsoncole or command-line tools : Calling JMX MBean method from a shell script
Here is an example uf using one of these tools - jmxterm
$>run -d org.springframework.boot: -b org.springframework.boot:name=shutdownEndpoint,type=Endpoint shutdown
#calling operation shutdown of mbean org.springframework.boot:name=shutdownEndpoint,type=Endpoint with params []
#operation returns:
{
message = Shutting down, bye...;
}

Unable to register JMX Metric Dropwizard

I am trying to add JMX metric in my Dropwizard application, But when I am trying make it up locally it gives me error. I added following in my Application class in initalize method :
guiceBundle.addModule(new MetricsInstrumentationModule(bootstrap.getMetricRegistry()))
bootstrap.addBundle(new JmxMetricsBundle());
and in guice module file, I have added provider:
#Provides
#Singleton
MetricRegistry providesMetricRegistry(Environment environment) {
return environment.metrics();
}
added below dependency in pom.xml
<dependency>
<groupId>com.palominolabs.metrics</groupId>
<artifactId>metrics-guice</artifactId>
<version>3.1.3</version>
</dependency>
In my class I am using it below way:
private Timer getTimer(String name) {
return metricRegistry
.timer(MetricRegistry.name(DocumentService.class, name));
}
public void method(){
final Timer.Context context = getTimer("methodMetric").time();
try{
//do something
}finally {
context.stop();
}
}
but when I am making my service up it is throwing following error:
DEBUG [2017-06-02 09:01:58,059] com.codahale.metrics.JmxReporter:
Unable to register timer! javax.management.InstanceAlreadyExistsException:
metrics:name=io.dropwizard.jetty.MutableServletContextHandler.connect-requests
DEBUG [2017-06-02 09:01:58,066] com.codahale.metrics.JmxReporter:
Unable to register gauge! javax.management.InstanceAlreadyExistsException:
metrics:name=io.dropwizard.jetty.MutableServletContextHandler.percent-
4xx-1m
And similar error messages, although my application is getting up successfully. I have no idea why this error is occurring, is it some localhost setup issue which will not happen on production or some genuine issue which I am missing. please help.
EDIT:
JmxMetricsBundle class:
public class JmxMetricsBundle implements Bundle {
public void initialize(Bootstrap<?> bootstrap) {
JmxReporter reporter = JmxReporter.forRegistry(bootstrap.getMetricRegistry()).build();
reporter.start();
}
#Override
public void run(Environment environment) {
}
}

Weblogic,EJB, $Proxy99 class cast exception

Following are the modules in my project,
1. EJB module (version 3): We prepare ejb jar of this module and deploy on Weblogic11g server. It deals with database operation. It has #local, #Remote interface and #stateless classes implementing #local,#Remote interfaces.
2. Web Application : This web application takes inputs (user uploads file) from users, validates file and inserts data into database. It uses RMI.
Problem: On production (weblogic 11g server ) sometimes we observe exception saying $Proxy99 cannot be cast to "Remote interface name" (for different different classes) e.g com.xyz.fileProcessSetting.FileProcessSttgFacadeRemote.
But after some time when we again upload file, it gets uploaded successfully without any error.
Now, I do not understand how come these remote objects becomes temporarily unavailable? Never faced this issue on development/UAT environment. Also no idea how to reproduce and fix it.
Please help. Thanks in advance.
#Remote
public interface FileProcessSttgFacadeRemote {
//methods
}
#Local
public interface FileProcessSttgFacadeLocal {
//methods
}
#Stateless
public class FileProcessSttgFacade implements FileProcessSttgFacadeLocal, FileProcessSttgFacadeRemote {
//methods
}
in weblogic-ejb-jar.xml
<weblogic-enterprise-bean>
<ejb-name>FileProcessSttgFacade</ejb-name>
<stateless-session-descriptor>
<business-interface-jndi-name-map>
<business-remote>com.xyz.fileProcessSetting.FileProcessSttgFacadeRemote</business-remote>
<jndi-name>FileProcessSttgFacade</jndi-name>
</business-interface-jndi-name-map>
</stateless-session-descriptor>
</weblogic-enterprise-bean>
In web application also in ejb module whenever we want to call methods we use following lookup method to get remote object:
public class someclass extends EjbLocator {
public void someMethod(){
FileProcessSttgFacadeRemote fpfr = (FileProcessSttgFacadeRemote) getService("FileProcessSttgFacade");
//other code
}
}
Following is the class used for JNDI lookup:
public class EjbLocator {
public Object getService(final String jndiName) throws Exception {
try {
obj = getDefaultContext().lookup(jndiName);
} catch (final Exception exp) {
exp.printStackTrace();
}
return obj;
}
protected Context getDefaultContext() {
try {
final Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL,"weblogic");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put(Context.PROVIDER_URL, "t3://<ip>:<port>");
defaultContext = new InitialContext(env);
return defaultContext;
} catch (final NamingException nExp) {
nExp.printStackTrace();
}
return null;
}
}

org.osgi.service.event.EventHandler fails to listen the events posted by the EventAdmin service

I am trying to run a demo application for OSGi EventAdmin service, but the EventHandler I implemented fails to listen the events posted by the EventAdmin publisher:
Below is the code for Event Publisher, followed by the code for Listener(EventHandler):
public class Publisher implements BundleActivator{
static EventAdmin eventAdmin;
ServiceReference ref;
static HashMap properties= null;
#Override
public void start(BundleContext context) throws Exception {
ref=context.getServiceReference(EventAdmin.class.getName());
if(ref==null){
System.err.println("Unable to aquire EventAdmin Ser Ref.");
}
eventAdmin=(EventAdmin) context.getService(ref);
if(eventAdmin==null){
System.err.println("unable to get service:EventAdmin");
}
properties=new HashMap();
properties.put("XYZ", "Test");
Event event = new Event("lnu/test/event/Demo", properties);
eventAdmin.postEvent(event);
System.out.println("event posted");
}
#Override
public void stop(BundleContext context) throws Exception {
// TODO Auto-generated method stub
}
}
Code for Listener:
public class Listener implements BundleActivator, EventHandler {
public void start(BundleContext context) {
Dictionary d = new Hashtable();
d.put(EventConstants.EVENT_TOPIC, "lnu/test/event/Demo" );
context.registerService( EventHandler.class.getName(),
this, d );
System.out.println("event handler is registered now");
}
public void stop( BundleContext context) {}
public void handleEvent(Event event ) {
System.err.println("Event has been captured");
System.out.println("getTopic: "+event.getTopic());
System.out.println("getproperty: "+event.getProperty("XYZ"));
}
}
The print statements in the code show that the event has been posted by the publisher and the Listener is registered with the EventHandler service but still it does not invokes handleEvent method on the listener side, I don't know why? and can't understand what is happening behind the scene. There are no runtime exceptions/errors.
The IDE used is Eclipse Juno Build id: 20120614-1722 with Equinox.
Following Target Platform bundles are included in the run configuration:
org.eclipse.osgi
org.eclipse.equinox.event
org.eclipse.equinox.util
org.eclipse.osgi.services
Can some one point me what I am missing or doing wrong? Or if you have some link to working example of OSGi EventAdmin service?
I would guess that your listener bundle is being registered after the publisher bundle has already posted the Event.
Testing this in the start methods of the bundles is error prone for this reason unless you control the start order of the bundles. I would suggest for this simple test that you start a separate thread in your publisher to post an event every few seconds. The listener should start getting them once it is registered.
Confirm that your listener bundle is importing the same org.osgi.service.event package as the EventAdmin bundle. It is possible that your listener bundle includes the org.osgi.service.event package and is thus not using the same org.osgi.service.event package as the EventAdmin bundle. This could be why the EventAdmin bundle does not call your EventHandler service. It may be something else, but this is something to check.

Resources