I'm currently developing a plugin system in which I embed apache felix in my application. The plugins itself are the OSGi bundles. So far deploying the bundles works just fine but I have trouble interacting with my bundles/plugins. I tried two approaches:
Register the a service "Plugin" in my Plugin and use the service listener in my "host" application to interact with the plugins.
The service listener is not invoked and I can't cast the returned Plugin object because the Plugin.class of my Host application is a different one compared to the Plugin.class thats inside the bundle.
Register the "PluginManager" in the host application and load this manager in the bundle.
In this case I'm again unable to cast the service class because of this class "duplication" issue.
I understand why the classes are "duplicated" but I'm not sure what to do about it.
My current setup:
plugin-api maven module: Provides Plugin interface
app maven module: Contains the app which embeds Apache Felix
dummy plugin has only a dependency on plugin-api
Is there a problem with the way my setup is structured? How can I access host services without creating a class mess? Should I create another module which is used to compile my plugin but it is excluded from the bundle and later provided on the host via FRAMEWORK_SYSTEMPACKAGES_EXTRA?
You should define your Plugin API (and all the non-VM based types that it uses) on the application side. If I would do this, I would make an API bundle (yes bundle) that exports these packages.
Make sure that all plugins not export the API or at least allow it to be imported.
In your application, before you start your Felix embedded framework, you get all the manifests of all JARs on the classpath with getResources("META-INF/MANIFEST.MF")and check for Export-Package. Then concatenate all these exported packages and set the OSGi Framework property org.osgi.framework.system.packages.extra to the joined string.
This will export any package on your classpath, so also your API bundle. Since the framework now exports these packages, your plugins will use the standard classpath as provider. Therefore, the API will have only one source and you will not get into class hell.
Related
I'm writing custom Keycloak provider which needs quarkus-cxf extension as a dependency to integrate with a SOAP service. Everything works well in the development mode (I use maven, so I needed to add quarkus-cxf as a dependency and run Keycloak on Quarkus from IDE using IDELauncher).
The problem starts when I'm trying to deploy my custom provider to the production environment. I know that my provider's jar need to be placed in providers directory within Keycloak distribution. Somewhere on Keycloak webpage I saw that every third-party dependencies jars I use, need to be placed in providers directory as well, so I did that and (thanks to maven-dependency-plugin) I generated all quarkus-cxf dependencies and put them all into providers directory. Then I tried to build keycloak by running ./kc.bat build and it failed with the message: ERROR: io.smallrye.config.SmallRyeConfigFactory: io.quarkus.runtime.configuration.QuarkusConfigFactory not a subtype.
How it should be done to work properly? I need CXF dependencies in the build phase because I generate code from WSDL file and it's necessary for Quarkus augmentation. Maybe not all dependencies from quarkus-cxf dependency hierarchy are needed here, but I cannot imagine developing and then adding one by one jar and check if it's enough for build or maybe not and I will get NoClassDefFound error :) I've got the same problem with my previous extension where I used resteasy dependencies but then only few jars were missing so I put them into providers directory and it worked. It's not the best solution for each and every one extension with extra dependencies like cxf (with huge hierarchy of transient dependencies).
Does anyone help me in this case? Maybe some of you have the same problem and you know how it should be done.
So far, I have created a web application using JAX-RS (Jersey) and Maven as build and dependency managment, but for this question, I'm not sure it matters. I'm using h2k as DI framework. It works fine and I can package the application as a WAR which can be deployed to a tomcat server (both locally and remote).
The application is configured using jersey's ResourceConfig, where I also configure my AbstractBinder (for h2k) to bind my #Inject to concrete instances. So far so good. Now I want to use Jetty (or grizzly) as an embedded server for local development (by mvn jetty:run), and automate the build of the war for remote deployment. I want to use different classes (injected by hk2) depending on the environment (eg. fake email sender, on test server), and this is where I'm stuck. How do I specify which environment I'm running in and how do I specify which classes to use for each environment?
Maybe my problem is in my understanding of how all this works (Examples of actual build setups would be warmly welcomed). Normally I just use AbstractFactory, which I inject into my main method. My guess on how this should be done:
I should create a properties / xml file for each environment, where I specify which implementations and properties should be used.
When running or building, I should specify which environment I'm running in (for instance mvn build -ENVIRONMENT)
For a java project I want to spin up a server application during integration test (maven-failsafe-plugin, can be switched).
Problem
The server application should be fetched via maven
My project and the server depend on a shared library
The version of my copy of the shared lib may be different then the one of the server application
(During test there is even a third application involved, but the same requirements apply)
Current solution
Create a classloader manually, built classpath manually, start server application in custom classpath
manual dependency resolution sucks. Has to be redone on dependency changes.
Put everything onto the classpath, remove stuff that breaks.
Also manual...
Wish
Specify a "dependency profile" in pom.xml for each component
During test call something like: Maven.getClassLoaderForProfile("server"), receiving a classloader with all dependencies (including transitive ones)
Load application in this classloader
I just start learning how to build a bnd OSGI project.
I try to run a very simple project without any error message,but when I go to localhost, it shows "HTTP ERROR: 404".
the simple class:
an Activator class:
rest build dependencies
Run dependencies
http error:
Thanks for your helps!!
The latest 2.0.4 release of the org.amdatu.web.rest.wink bundle doesn't play well with Felix Http Jetty 3.x.
If you pin the version of that bundle to the 2.0.3 version things should work as expected. To do this change the org.amdatu.web.rest.wink entry your runbnd.bndrun -runbundles to:
org.amdatu.web.rest.wink;version='[2.0.3,2.0.3]'
Your class is annotated with jax-rs annotations and publishes an OSGi service. If this exposes the services as a REST resources depends on the bundles you install.
You have to install a bundle that watches for such services and creates the REST endpoints for them.
See enter link description here
I think you at least need to also add the org.amdatu.web.wink bundle to your bdnrun file.
I am writing an application plugin in Java, and my plugin has dependencies on several third-party JARs. I am bundling these dependencies with my plugin so that I can deploy just a single JAR file.
The host application may also be running plugins from other vendors. Unfortunately the host application puts all the plugins on the classpath, and I am not able to change this behavior. If another vendor's plugin is loaded before mine and uses an incompatible version of a dependency, my plugin could crash.
I am not able to test compatibility between my plugin and other plugins ahead of time. It is also not acceptable for me to say that there is an incompatibility between the plugins--if my plugin crashes, it reflects poorly on my company. The customer does not care why my plugin crashes, they will attribute it to poor programming on my end.
I am looking for a way to prevent other vendors' plugins from interfering with my own. Is it possible?
I've heard of custom classloaders but I'm not sure if that solution will work for me.
You can use Uberjar. What it does is move all your jars/classes to a custom namespace so that none of your classes clash because your dependencies have a different namespace.
You might want to look at maven-shade
You could try to embed an OSGi container in your plugin. This would allow you to run and load dependencies as OSGi bundles in isolation from the system classloader.
Instructions for Felix.