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

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.

Related

Order of execution between CommandLineRunner run() method and RabbitMQ listener() method

My Spring Boot application is subscribing to an event via RabbitMQ.
Another web application is responsible for publishing the event to the queue which my application is listening to.
The event basically contains institute information.
The main application class implements CommandLineRunner and overrides run() method.
This run() method invokes a method to create admin user.
When my application is started, and when an event is already present in queue, the listener in my application is supposed to update the Admin user's institute id.
However it looks like createAdmin() and the listener() are getting executed in parallel and institute id is never updated. Help me in understanding the control flow.
See below the code snippet and the order of print statements.
#SpringBootApplication
public class UserManagementApplication implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(UserManagementApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
createAdmin();
}
private void createAdmin() {
System.out.println("************** createAdmin invoked *********************");
Optional<AppUserEntity> user = appUserService.getUserByUserName("superuser");
if(!user.isPresent()) {
AppUserEntity superuser = new AppUserEntity();
superuser.setUsername("superuser");
superuser.setAppUserRole(AppUserRole.SUPERADMIN);
superuser.setInstId(null); // will be set when Queue receives Institute information
appUserService.saveUser(superuser);
System.out.println("************** superuser creation SUCCESSFUL *********************");
}
}
}
#Component
public class InstituteQueueListener {
#RabbitListener(queues = "institute-queue")
public void updateSuperAdminInstituteId(InstituteEntity institute) {
System.out.println("************** RabbitListener invoked *********************");
Long headInstituteId = institute.getInstId();
Optional<AppUserEntity> user = appUserService.getUserByUserName("superuser");
if(user.isPresent()) {
System.out.println("************* superuser is present *****************");
AppUserEntity superuser = user.get();
superuser.setInstId(headInstituteId);
System.out.println("************* Going to save inst Id = "+headInstituteId);
appUserService.saveUser(superuser);
}
System.out.println("************** superuser is NOT present (inside Q listener)*********************");
}
}
Order of print statements ....
(the queue already has event before running my application)
System.out.println("************** createAdmin invoked *********************");
System.out.println("************** RabbitListener invoked *********************");
System.out.println("************** superuser is NOT present (inside Q listener) *********************");
System.out.println("************** superuser creation SUCCESSFUL *********************");
When you start your application, any CommandLineRunners are called on the main thread (the thread on which you called SpringApplication.run). This happens once the application context has been refreshed and all of its beans have been initialized.
#RabbitListener-annotated methods are called by a message listener container once the container has been started and as messages become available. The container is started as part of the application context being refreshed and, therefore, before your command line runner is called. The container uses a separate pool of threads to call its listeners.
This means that your listener method may be called before, at the same time, or after your command line runner, depending on whether there is a message (event) on the queue.

How to register service at UWP project

I am trying to register alert service ( message dialog) at setup.cs for this method
protected override void InitializePlatformServices() {
base.InitializePlatformServices();
Mvx.RegisterSingleton<IAlertService>(new AlertService());
}
but i also have this exception http://prntscr.com/krpbcw
For services which has implementation in native projects.
Go to Setup.cs
protected override IMvxApplication CreateApp(){
Mvx.RegisterType< IAlertService, AlertService >();
}
Know one thing, AlertService implements IAlertService

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.

Inject HttpService in an OSGi bundle in JBoss 7

I have a JBoss 7.1.2 application server up and running and now I wanted to deploy a OSGi bundle in it which registers a servlet. (kind of Hello World, but via a servlet)
Probably it is possible to archive this via a WAB, but as in OSGi, I thought it should also work in a simple, non wab bundle.
I created a bundle with an Activator as follows:
snip..
public void start(BundleContext bundleContext) throws Exception {
ServiceReference<?> httpServiceReference = bundleContext.getServiceReference(HttpService.class.getName());
System.out.println("ref: " + httpServiceReference);
HttpService httpService = (HttpService) context.getService(httpServiceReference);
System.out.println(httpService.getClass().getName());
try {
httpService.registerServlet("testservlet", new MyServlet() , null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
After deploying and starting the bundle, the httpServiceReference is null.
I think this is strange, because when I look at the felix web console, there actually is HtttpService available. (from pax-web)
Does anyone know why my reference could be null here?
Thanks!
I think you are making the classical OSGi mistake. In the start(BundleContext context) method you are obtaining the reference to HttpService assuming its already started. The OSGi container starts bundles in some order. If your application bundle starts before the bundle that registers HttpService you will get a null reference. I suspect that's what is going on. You might be better off using ServiceTracker instead.
public void start(BundleContext bundleContext) throws Exception {
ServiceTracker httpServiceTracker = new ServiceTracker(bundleContext, HttpService.class, new ServiceTrackerCustomizer() {
#Override
public Object addingService(ServiceReference srf) {
HttpService httpService = bundleContext.getService(srf);
try {
httpService.registerServlet("testservlet", new MyServlet() , null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void modifiedService(ServiceReference arg0, Object arg1) {
}
#Override
public void removedService(ServiceReference arg0, Object arg1) {
}
});
httpServiceTracker.open();
}

Unit Testing Spring ApplicationEvents - Events are getting published but the listeners aren't firing?

I'm trying to unit test the custom events that I've created in Spring and am running into an interesting problem. If I create a StaticApplicationContext and manually register and wire the beans I can trigger events and see the program flow through the publisher (implements ApplicationEventPublisherAware) through to the listener (implements ApplicationListener<?>).
Yet when I try to create a JUnit test to create the context using the SpringJunit4ClassRunner and #ContextConfiguration everything works well except that the ApplicationEvents are not showing up in the listener (I have confirmed that they are getting published).
Is there some other way to create the context so that ApplicationEvents will work correctly? I haven't found much on the web about unit testing the Spring events framework.
The events will not fire because your test classes are not registered and resolved from the spring application context, which is the event publisher.
I've implemented a workaround for this where the event is handled in another class that is registered with Spring as a bean and resolved as part of the test. It isn't pretty, but after wasting the best part of a day trying to find a better solution I am happy with this for now.
My use case was firing an event when a message is received within a RabbitMQ consumer. It is made up of the following:
The wrapper class
Note the Init() function that is called from the test to pass in the callback function after resolving from the container within the test
public class TestEventListenerWrapper {
CountDownLatch countDownLatch;
TestEventWrapperCallbackFunction testEventWrapperCallbackFunction;
public TestEventListenerWrapper(){
}
public void Init(CountDownLatch countDownLatch, TestEventWrapperCallbackFunction testEventWrapperCallbackFunction){
this.countDownLatch = countDownLatch;
this.testEventWrapperCallbackFunction = testEventWrapperCallbackFunction;
}
#EventListener
public void onApplicationEvent(MyEventType1 event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
#EventListener
public void onApplicationEvent(MyEventType2 event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
#EventListener
public void onApplicationEvent(OnQueueMessageReceived event) {
testEventWrapperCallbackFunction.CallbackOnEventFired(event);
countDownLatch.countDown();
}
}
The callback interface
public interface TestEventWrapperCallbackFunction {
void CallbackOnEventFired(ApplicationEvent event);
}
A test configuration class to define the bean which is referenced in the unit test. Before this is useful, it will need to be resolved from the applicationContext and initialsed (see next step)
#Configuration
public class TestContextConfiguration {
#Lazy
#Bean(name="testEventListenerWrapper")
public TestEventListenerWrapper testEventListenerWrapper(){
return new TestEventListenerWrapper();
}
}
Finally, the unit test itself that resolves the bean from the applicationContext and calls the Init() function to pass assertion criteria (this assumes you have registered the bean as a singleton - the default for the Spring applicationContext). The callback function is defined here and also passed to Init().
#ContextConfiguration(classes= {TestContextConfiguration.class,
//..., - other config classes
//..., - other config classes
})
public class QueueListenerUnitTests
extends AbstractTestNGSpringContextTests {
private MessageProcessorManager mockedMessageProcessorManager;
private ChannelAwareMessageListener queueListener;
private OnQueueMessageReceived currentEvent;
#BeforeTest
public void Startup() throws Exception {
this.springTestContextPrepareTestInstance();
queueListener = new QueueListenerImpl(mockedMessageProcessorManager);
((QueueListenerImpl) queueListener).setApplicationEventPublisher(this.applicationContext);
currentEvent = null;
}
#Test
public void HandleMessageReceived_QueueMessageReceivedEventFires_WhenValidMessageIsReceived() throws Exception {
//Arrange
//Other arrange logic
Channel mockedRabbitmqChannel = CreateMockRabbitmqChannel();
CountDownLatch countDownLatch = new CountDownLatch(1);
TestEventWrapperCallbackFunction testEventWrapperCallbackFunction = (ev) -> CallbackOnEventFired(ev);
TestEventListenerWrapper testEventListenerWrapper = (TestEventListenerWrapper)applicationContext.getBean("testEventWrapperOnQueueMessageReceived");
testEventListenerWrapper.Init(countDownLatch, testEventWrapperCallbackFunction);
//Act
queueListener.onMessage(message, mockedRabbitmqChannel);
long awaitTimeoutInMs = 1000;
countDownLatch.await(awaitTimeoutInMs, TimeUnit.MILLISECONDS);
//Assert - assertion goes here
}
//The callback function that passes the event back here so it can be made available to the tests for assertion
private void CallbackOnEventFired(ApplicationEvent event){
currentEvent = (OnQueueMessageReceived)event;
}
}
EDIT 1: The sample code has been updated with CountDownLatch
EDIT 2: Assertions didn't fail tests so the above was updated with a different approach**
I just run my app as SpringBootTest, application events working fine:
#TestComponent
public class EventTestListener {
#EventListener
public void handle(MyCustomEvent event) {
// nothing to do, just spy the method...
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyEventTest {
#SpyBean
private EventTestListener testEventListener;
#Test
public void testMyEventFires() {
// do something that fires the event..
verify(testEventListener).handle(any(MyCustomEvent.class));
}
}
use the #Captor / ArgumentCaptor to verify the content of your event.
You can create a context manually.
For example: I had needed to check if my ApplicationListener<ContextClosedEvent> closed Cassandra connections:
#Test
public void testSpringShutdownHookForCassandra(){
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CassandraConfig.class);
CassandraConnectionManager connectionManager = ctx.getBean(CassandraConnectionManager.class);
Session session = connectionManager.openSession(testKeySpaceName);
Assert.assertFalse( session.isClosed() );
ctx.close();
Assert.assertTrue( session.isClosed() );
}

Resources