Using Felix Dependency Manager to create Configuration Dependeny - osgi

I'm trying to get the FelixDependencyManager working.
I'm using Karaf 3 and i'm testing with 2 Bundles, one to provide the configuration information and one implementing the ManagedService-Interface and depending on this information.
So everything boils down to 3 classes:
The dependent bundle has 2 classes:
the first one is extending the DependencyActivatorBase class needed for using the Felix DependencyManager and implements the init method, where the component is created and configured:
public class ConfigAdminTester extends DependencyActivatorBase{
#Override
public void init(BundleContext context, DependencyManager manager)
throws Exception {
manager.add(createComponent()
.setImplementation(ConfigTestImpl.class)
.add(createConfigurationDependency()
.setPid("test.configadmintest.testconfig"))
.setCallbacks(null, "start", null, null)
);
System.out.println("ConfigAdminTester init finished");
}
#Override
public void destroy(BundleContext context, DependencyManager manager)
throws Exception {
}
}
The second class in the bundle is the one that should get updated by the ConfigAdmin-Service. It implements the ManagedService and has the updated-method, which should get called when deploying the bundle and when updating the configuration:
public class ConfigTestImpl implements ManagedService{
String host;
String port;
int id;
String password;
public void start(){
System.out.println("Host, Port, Id, Password: " + host + ", " + port +", " + id + ", " + password);
}
#Override
public void updated(#SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
if (properties != null) {
host= ((String)properties.get("host"));
if (host == null) {
throw new ConfigurationException("host", "must be specified");
}
port=((String)properties.get("port"));
if (port == null) {
throw new ConfigurationException("port", "must be specified");
}
id=((Integer)properties.get("id"));
password=((String)properties.get("password"));
System.out.println("Configuration in Bundle HttpServer was updated");
}
else {
System.out.println("Properties are null");
}
}
}
The second bundle has only one class, which implements the BundleActivator, gets a Reference to the ConfigAdmin-Service and creates a new configuration with the same pid the other bundle uses:
public class ConfigCreator implements BundleActivator{
public void start(BundleContext context) throws Exception {
#SuppressWarnings("rawtypes")
ServiceReference serviceRef = context.getServiceReference(ConfigurationAdmin.class.getName());
#SuppressWarnings("unchecked")
ConfigurationAdmin configAdmin = (ConfigurationAdmin) context.getService(serviceRef);
Configuration config = configAdmin.getConfiguration("test.configadmintest.testconfig", null);
Dictionary <String, Object> properties = new Hashtable <String, Object>();
properties.put("host", "test");
properties.put("port", "test");
properties.put("id", 1);
properties.put("password", "test!");
config.update(properties);
System.out.println("config updated");
}
The problem now is that the updated method never gets called. I get the output that ConfigAdminTest init is finished and the Bundle gets activated, but the callback method start in the ConfigtestImpl never gets called. If i just declare the component without the Configuration dependency, everything works fine, the callback method is executed as it should be. The configuration is published, available (i checked with the config:list command) and valid, but it seems that somehow the configuration is not available for the dependent bundle.
Any one an idea?
Help greatly appreciated.

I literally copied the three classes you provided into my IDE, and made two bundles out of them. I also included a bundle that implements ConfigurationAdmin and the DependencyManager. When I started everything, I immediately got the message:
Configuration in Bundle HttpServer was updated
So I'm not sure what went wrong here, but your code seems to be absolutely correct! Let me show you to full output of my run, including the GoGo shell and DM shell commands:
ConfigAdminTester init finished
config updated
Configuration in Bundle HttpServer was updated
Host, Port, Id, Password: test, test, 1, test!
____________________________
Welcome to Apache Felix Gogo
g! lb
START LEVEL 1
ID|State |Level|Name
0|Active | 0|System Bundle (4.2.1)
1|Active | 1|Apache Felix Configuration Admin Service (1.8.0)
2|Active | 1|Apache Felix Dependency Manager (3.1.0)
3|Active | 1|Apache Felix Dependency Manager Shell (3.0.1)
4|Active | 1|Apache Felix Gogo Command (0.12.0)
5|Active | 1|Apache Felix Gogo Runtime (0.10.0)
6|Active | 1|Apache Felix Gogo Shell (0.10.0)
7|Active | 1|osgi.cmpn (4.3.1.201210102024)
8|Active | 1|xxx.stackoverflow.b1 (0.0.0)
9|Active | 1|xxx.stackoverflow.b2 (0.0.0)
g! dm
[8] xxx.stackoverflow.b1
class b1.ConfigTestImpl registered
test.configadmintest.testconfig configuration required available
g!
Just to be sure, I did place the code in two different packages. The sample you provided does not show packages or imports, but you cannot use the default (empty) package in OSGi. Another mistake could have been some wrong import. Anyway, if you're still stuck, let me know, and I'll be happy to provide the full sourcecode.

Related

Refresh Package Bundles

I've stumbled upon a problem, that can be summarized as follows:
I have an application which is embedded with OSGI. In this OSGI container, I have installed a bundle A of version v1 which registers that service object. This service object is used by the host application. while the service object is in use, I uninstalled the bundle A and installed a new version(v2) of the bundle A. Here are my findings.
Old service object works fine even if we uninstall the bundle A(v1).
The new service object gives the new functionality of bundle A(v2).
My question here is when we refresh the bundle packages by using
List<Bundle> refreshbundles = new ArrayList<Bundle>();
Bundle local; // points to the bundle
refreshbundles.add(local);
m_felix.adapt(FrameworkWiring.class).refreshBundles(refreshbundles)
and when we try to get the service object registered by the new version of the bundle A, it is returning null. I tried looking at the source code but it didn't help. can you please help me with this?
EDIT:
HostApplication
public Felix m_felix = null;
m_felix = new Felix(config); //sending some config to felix.
m_felix.start();
//installing bundle
Bundle bundle = m_felix.getBundleContext().installBundle("file:/bundleA-1.0.jar")
bundle.start()
//getting the service object registered by bundle A.
ServiceReference sr = m_felix.getBundleContext()
.getServiceReference(SampleInterface.class.getName()))
(SampleInterface) m_felix.getBundleContext(sr).getService().sayHI();
//now uninstalling the bundle installing the new version of it say version 1.1
bundle.uninstall()
bundle = m_felix.getBundleContext().installBundle("file:/bundleA-1.1.jar")
bundle.start()
//getting the service object registered by bundleA1.1
ServiceReference sr = m_felix.getBundleContext()
.getServiceReference(SampleInterface.class.getName()))
(SampleInterface) m_felix.getBundleContext(sr).getService().sayHI();
//the above line is working fine but after refreshing the packages, Service object is returned as null
List<Bundle> refreshbundles = new ArrayList<Bundle>();
Bundle bundle; // points to the bundle
refreshbundles.add(bundle);
m_felix.adapt(FrameworkWiring.class).refreshBundles(refreshbundles)
//refresh done
ServiceReference sr = m_felix.getBundleContext()
.getServiceReference(SampleInterface.class.getName()))
(SampleInterface) m_felix.getBundleContext(sr).getService().sayHI();
//throwing null pointer exception because getService() is returning null.
what exactly is happening when we refresh the bundles?
BundleA_Activator.java
public class BundleA_Activator extends BundleActivator{
public class BundleActivatorInterfaces implements BundleActivator{
ServiceRegistration SR;
#Override
public void start(BundleContext bundleContext) {
SR = bundleContext.registerService(SampleInterface.class.getName(), new ExposedClass(), null);
}
#Override
public void stop(BundleContext bundleContext) {
SR.unregister();
}
}

Remote OSGI using ECF: Bundle Requirements For BND

Can some one point what bundles am I missing in this bndrun file for registering a remote service in OSGI framework. I followed the following example in the link Building your first remote osgi service.
I have registered a service like this:
public void start(BundleContext context) throws Exception {
Dictionary<String, String> props = new Hashtable<>();
props.put("service.exported.interfaces", "*");
props.put("service.exported.configs", "ecf.generic.server");
context.registerService(ITimeService.class.getName(),
new TimeServiceImpl(), props);
}
Here is my runrequires section of bndrun file:
runrequires: \
osgi.identity;filter:='(osgi.identity=org.siu.casa.timeservice.host.org.siu.casa.timeservice.host.api.impl)',\
osgi.identity;filter:='(osgi.identity=org.siu.casa.timeservice.host.org.siu.casa.timeservice.api)',\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.runtime)',\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\
osgi.identity;filter:='(&(osgi.identity=org.osgi.service.event)(version>=1.3.1))',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf)',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf.discovery)',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf.identity)',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf.osgi.services.remoteserviceadmin)',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf.osgi.services.remoteserviceadmin.proxy)',\
osgi.identity;filter:='(osgi.identity=org.eclipse.ecf.provider.remoteservice)',\
osgi.identity;filter:='(&(osgi.identity=org.eclipse.equinox.registry)(version>=3.5.400))'

Cannot start bundle programmatically

Note: In my case, I'm using Apache Felix implementation if that's matters.
I have written bundle which I'm using as test. It's very simple "Hello World" bundle that do nothing more than print message to stdout when started/stopped:
public class Activator implements BundleActivator {
#Override
public void start(BundleContext context) throws Exception {
System.out.println("Hello, World.");
}
#Override
public void stop(BundleContext context) throws Exception {
System.out.println("Goodbye, World.");
}
}
There is also MANIFEST file which rather pointless to post since when I deploy above bundle through Apache Felix console from standard distribution (which can be downloaded here) bundle starts and print out message.
Next step I'm trying to do is deploy the very same bundle using programmatic approach. Unfortunately this is not working for me. My code looks as follow:
public static void main(String[] args) throws Exception {
FrameworkFactory frameworkFactory = getFrameworkFactory();
Framework framework = frameworkFactory.newFramework(null);
System.out.println("BundleID = " + framework.getBundleId());
System.out.println("State = " + getState(framework.getState()));
framework.init();
System.out.println("BundleID = " + framework.getBundleId());
System.out.println("State = " + getState(framework.getState()));
BundleContext bundleContext = framework.getBundleContext();
bundleContext.addBundleListener((event) -> {
System.out.println("Bundle Changed Event");
});
bundleContext.addFrameworkListener((event) -> {
System.out.println("Framework Event");
});
bundleContext.addServiceListener((event) -> {
System.out.println("Service Changed Event");
});
Bundle bundle = bundleContext.installBundle("file://<absolute-path-to-bundle-jar-same-as-above");
System.out.println("BundleID = " + bundle.getBundleId());
System.out.println("State = " + getState(bundle.getState()));
bundle.start();
System.out.println("BundleID = " + bundle.getBundleId());
System.out.println("State = " + getState(bundle.getState()));
}
private static FrameworkFactory getFrameworkFactory() throws IllegalStateException {
ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class);
FrameworkFactory factory = null;
for (FrameworkFactory iterator : loader) {
if (factory != null) {
throw new IllegalStateException("Ambiguous SPI implementations.");
}
factory = iterator;
}
return factory;
}
private static String getState(int state) {
switch (state) {
case Bundle.UNINSTALLED:
return "UNINSTALLED";
case Bundle.INSTALLED:
return "INSTALLED";
case Bundle.RESOLVED:
return "RESOLVED";
case Bundle.STARTING:
return "STARTING";
case Bundle.STOPPING:
return "STOPPING";
case Bundle.ACTIVE:
return "ACTIVE";
default:
throw new IllegalStateException("Unknown state");
}
}
The output looks like follow:
BundleID = 0
State = INSTALLED
BundleID = 0
State = STARTING
Bundle Changed Event
BundleID = 1
State = INSTALLED
BundleID = 1
State = INSTALLED
So as far as I understand bundle got installed but last 4 lines indicate that bundle.start() got ignored for some reason.
Could you point out me what am I missing to make this work?
After hour of debugging and reading through javadoc more carefully this is happening because framework was only initialized instead of being started. To make example work you have to simply add framework.start() after framework.init() (or just call framwork.start() which calls framework.init() if found it necessary).
I'm leaving this information as there are few confusing things:
Official documentation to Apache Felix have information about embedding framework into host application. Unfortunately there is only example that use Apache Felix custom mechanisms that make it not portable to other implementations. What is confusing is warning note which if you want to create portable solution you should use init() and getBundleContext(). Whole note cited bellow:
WARNING The felix.systembundle.activators configuration property is specific to the Felix framework implementation. If you want your code to work with other framework implementations, you should call init() on the framework instance and use getBundleContext() directly. Otherwise, the approach would be very similar.
JavaDoc for parameterless version of init() method do not mention about initialization is not same as starting the framework, although init(FrameworkListener...) have such information.
This Framework will not actually be started until start is called.

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.

Karaf PaxExam getting java.lang.IllegalStateException: Invalid BundleContext

Hi i am loading some features and bundles in runtime using FeaturesService and BundleContext. All these things are loaded successfully. After that if i do an operation on bundlecontext object i am getting
java.lang.IllegalStateException: Invalid BundleContext.
#Inject
FeaturesService service;
#Before
public void init() throws Exception{
service.installFeature("hibernate");
service.installFeature("hibernate-validator");
service.installFeature("transaction");
service.installFeature("jpa");
service.installFeature("hibernate-envers");
service.installFeature("hibernate-envers");
bc.installBundle("wrap:mvn:com.oracle/ojdbc6/11.2.0").start();
service.installFeature("DBHandler");
bc.getBundle(); // Fails
}
After a lot of browsing i understood you need to refresh the bundles. How to do it programatically and get a refeshed bundleContext object
You have this exception when you are using a bundle which is not valid : it has been stopped, or refreshed (a refresh stop the bundle and start a new instance)
When you install a feature, by default, Karaf try to define a list of bundles to refresh because of the new capabilities. For example, if a bundle have an optional dependency on a package, and the new feature add this package, then this bundle will be refreshed, in order to update his wires. This is transitive : all dependent bundles are refreshed too.
Moreover, when you use the "wrap" protocol, it create a bundle from a jar by importing all used packaged with a resolution 'optional'
In your case, I suppose the feature 'DBHandler' add a package which is used by your bundle.
You can :
After installing the features, look up your bundle by SymbolicName, with the BundleContext.getBundles() : You will have an instance of a valid bundle
Use the option NoAutoRefreshBundles to disable the refresh when installing a feature (featureService.installFeature("..", EnumSet.of(FeatureService.NoAutoRefreshBundles))). But it's not a good idea as some bundle will not see the new package
This code fixed my problem
public void refreshBundles() {
Bundle currentBundle = FrameworkUtil.getBundle(MyTest.class);
if (currentBundle == null) {
return;
}
BundleContext bundleContext = currentBundle.getBundleContext();
if (bundleContext == null) {
return;
}
Bundle systemBundle = bundleContext.getBundle(0);
if (systemBundle == null) {
return;
}
FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
frameworkWiring.refreshBundles(null);
bc = frameworkWiring.getBundle().getBundleContext();
}

Resources