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();
}
Related
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();
}
}
I'm trying to perform a user authentication from within an OSGi bundle deployed inside the IBM WebSphere Liberty server.
If I try
ctx = new LoginContext("system.DEFAULT", handler);
ctx.login();
I get the exception:
javax.security.auth.login.LoginException: unable to find LoginModule class: com.ibm.ws.kernel.boot.security.LoginModuleProxy cannot be found by ...
The same happens if I use
ctx = new LoginContext("WSLogin", handler);
ctx.login();
How can I properly use the LoginContext within an OSGi bundle ?
The correct way to achieve the result is quite similar:
final LoginContext ctx = new LoginContext("WSLogin",WSCallbackHandlerFactory.getInstance().getCallbackHandler(username, password));
ctx.login();
In the previous implementation the error was due to changing the calls loader before creating the login context.
The faulty code was:
final LoginContext ctx;
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
ctx = new LoginContext("RealmUsersRoles", handler);
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
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.
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.
I have JAX-WS container-less service (published via Endpoint.publish() right from main() method). I want my service to validate input messages. I have tried following annotation: #SchemaValidation(handler=MyErrorHandler.class) and implemented an appropriate class. When I start the service, I get the following:
Exception in thread "main" javax.xml.ws.WebServiceException:
Annotation #com.sun.xml.internal.ws.developer.SchemaValidation(outbound=true,
inbound=true, handler=class mypackage.MyErrorHandler) is not recognizable,
atleast one constructor of class
com.sun.xml.internal.ws.developer.SchemaValidationFeature
should be marked with #FeatureConstructor
I have found few solutions on the internet, all of them imply the use of WebLogic container. I can't use container in my case, I need embedded service. Can I still use schema validation?
The #SchemaValidation annotation is not defined in the JAX-WS spec, but validation is left open. This means you need something more than only the classes in the jdk.
As long as you are able to add some jars to your classpath, you can set this up pretty easily using metro (which is also included in WebLogic. This is why you find solutions that use WebLogic as container.). To be more precise, you need to add two jars to your classpath. I'd suggest to
download the most recent metro release.
Unzip it somewhere.
Add the jaxb-api.jar and jaxws-api.jar to your classpath. You can do this for example by putting them into the JAVA_HOME/lib/endorsed or by manually adding them to your project. This largely depends on the IDE or whatever you are using.
Once you have done this, your MyErrorHandler should work even if it is deployed via Endpoint.publish(). At least I have this setup locally and it compiles and works.
If you are not able to modify your classpath and need validation, you will have to validate the request manually using JAXB.
Old question, but I solved the problem using the correct package and minimal configuration, as well using only provided services from WebLogic. I was hitting the same problem as you.
Just make sure you use correct java type as I described here.
As I am planning to expand to a tracking mechanism I also implemented the custom error handler.
Web Service with custom validation handler
import com.sun.xml.ws.developer.SchemaValidation;
#Stateless
#WebService(portName="ValidatedService")
#SchemaValidation(handler=MyValidator.class)
public class ValidatedService {
public ValidatedResponse operation(#WebParam(name = "ValidatedRequest") ValidatedRequest request) {
/* do business logic */
return response;
}
}
Custom Handler to log and store error in database
public class MyValidator extends ValidationErrorHandler{
private static java.util.logging.Logger log = LoggingHelper.getServerLogger();
#Override
public void warning(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void error(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void fatalError(SAXParseException exception) throws SAXException {
handleException(exception);
}
private void handleException(SAXParseException e) throws SAXException {
log.log(Level.SEVERE, "Validation error", e);
// Record in database for tracking etc
throw e;
}
}