7 Replies Latest reply on Feb 19, 2013 2:03 PM by werrenmi

    Classloading OSGi / Modules

    werrenmi

      Hello together

       

      In our SOA environment we use internal Services they are deployed via OSGi and other ones per CDI in a war (because Switchyard). Now we need a bridge between this two approaches.

       

      I know this thread here: https://community.jboss.org/thread/172622

       

      But the reference of the BundleContext is not the problem.

       

      @Resource

      private BundleContext bc;

       

      works well in an EJB that is deployed in an jar within the war.

       

      Our problem is the classloading.

       

      When i try to obtain a service via this injected BundleContext (or otherwise lookup a service in a OSGi bundle thats added via this BundleContext in the web app), then a ClassCastException will be thrown. Thats clear because the module classloader of the war and the BundleContext reference are different ones at this point.

      After that i have debbuged to see which classloader this BundleContext has ... its the module loader of the module "org.jboss.osgi.framework". But adding this module to the "jboss-deployment-structure.xml" dont solve this issue.

       

      Is there a way in JBoss AS 7.1.1.Final to bring this to work?

       

      Thanks in advance.

       

      Regards Michel

        • 1. Re: Classloading OSGi / Modules
          werrenmi

          In my first post i did a mistake. My predication thats the BundleContext has a different classloader was not important. But the service that i try to get has the module classloader of the osgi bundle that register it. In my optinion, the problem is that (as far as i know) no way exists to add the bundle module as dependency to the "jboss-deployment-structure.xml" of the war. Or i am wrong?

          • 2. Re: Classloading OSGi / Modules
            ulrichromahn

            Hi Michel,

             

            I faced the exact same problem and believe I have solved it.

            There is, however, one big caveat to my solution: as far as I know, it only works in the latest JBoss AS 7.2.0-Alpha1-SNAPSHOT (the current master on Github).

             

            I believe what throws you off is the way the OSGi subsystem treats its deployments vs the JEE subsystem. I have just scratched the surfaced and don't believe I can explain this to you in more detail - Thomas Diesler should be able to explain that much better (if he choses to chime in on this discussion) since he made most of those changes for 7.2.0.

             

            With my limited knowlege, the big difference between 7.1.x and 7.2.x is that in 7.2.x they have "unified" the way different deployment units gets treated and hence allowing a much better integration between JBoss Modules and the OSGi subsystem.

            In 7.1.x it appears that every OSGi bundle is also a JBoss Module, but a JBoss Module was not necessarily a "first-class" OSGi bundle which may cause the pain you are experiencing.

            However, based on my tests, that has changed in 7.2.x and it seems that every JBoss Module that has a valid OSGi MANIFEST.MF can be added to the OSGi subsystem as a bundle.

             

            I have a small POC with a very simple OSGi service (two separate JARs - one is an API jar the other provides the implementation). I then expose the OSGi service to all non-OSGi deployments (WAR, EAR, EJB-JAR) and make them "injectable" via CDI. The way to get there was pretty "rocky" and involves some "hacks" that will couple your code pretty tightly to JBoss AS, since it uses unique JBoss features. But, I think this should be OK because the OSGi/JEE integration in JBoss AS is pretty unique in itself.

             

            Here is a high-level outline of what I did:

             

            1. I developed my two OSGi bundles implementing the Serivice API interface in one and the actual implementation in the second.
            2. The implementation bundle has a simple OSGi activator that registers my service with OSGi - standard OSGi stuff
            3. The API bundle actually gets "deployed" as a JBoss Module (with the corresponding module.xml) and then also "loaded" as OSGi bundle through a "capability" declaration in standalone.xml (or module.xml). In addition, I declared my API Module "public" in the JEE subsytems (also in the standalone.xml). That way, I don't have to add my service interface as dependency via MANIFEST.MF or jboss-deployment-structure.xml in every deployment (just being "lazy")
            4. The Service implementation will get deployed by just "dropping" the bundle jar into the standalone/deployments folder - that gives me the freedom to update dynamically or add new versions. The reasoning behind this separate deployment is that I expect my API to be much more stable than the actual implementation, hence the more static deployment of the API bundle as a hybrid Module/Bundle.
            5. The next step was a bit more complex and involved: I created a new JBoss subsystem (I called it OSGi-CDI-Bridge) with its own deployer. Additionally, I developed another library that contains a CDI portable extension (more on this here: http://docs.jboss.org/weld/reference/latest/en-US/html/extend.html). The only purpose of my new subsystem was to actually load the CDI portable extension and register it with Weld and provide a "universal deployer" which will make the service available to this deployment unit for injection.
            6. My portable extension now defined a service proxy class (implementing my service interface) which I wrapped as a managed bean (see chapter 16.7 in the Weld documentation above) and registered it with the CDI container (aka Weld) as an Injection Target.
            7. The last step was to add a valid OSGi manifest to my portable extension JAR and add it as OSGi "capability". In the OSGi Activator, I captured the BundleContext which will get referenced from the service proxy at the time of its instantiation through the bean manager (Note: CDI annotations do not seem to work inside a standard OSGi module, at least not for me, so I could not simply use @Resource BundleContext ctx).

            After all those rather complicated steps, I was able to inject a reference to my service (actually to my service proxy) into a SLSB inside a standard webapp (WAR). In order to provide your webapp access to those classes you obviously have to add the OSGI-CDI-Bridge module (the one that is also an OSGI bundle) as dependency to your deployment by either adding it as "Dependency" to the MANIFEST.MF, or by adding a joss-deployment-structure.xml, or - for the "lazy" ones - by making it a global module (in the jee subsytem inside standalone.xml).

             

            One little side comment regarding "hybrid" jars that are JBoss Modules as well as OSGi Bundles: if you happen to have a JBoss Module that consists of two or more JARs, only one of them can be actually activated as OSGi bundle.

            For example, you have a module called "com.acme.ab" which consists of two jars, a.jar and b.jar, only one of the two jars can be loaded and activated as OSGi bundle. Which one seems to be determined by the order in which they get listed in the module.xml.

            In the example above, let's assume both a.jar and b.jar have an OSGi manifest and an OSGi Activator. If the module.xml contains

            <module xmlns="urn:jboss:module:1.1" name="com.acme.ab">

                <resources>

                    <resource-root path="a.jar"/>

                    <resource-root path="b.jar"/>

                </resources>

            Then only a.jar will be loaded and activated in the OSGi subsystem if you added the module "com.acme.ab" as a capability to your OSGi subsystem in standalone.xml.

             

            Hope all of that is at least a bit helpful.

            1 of 1 people found this helpful
            • 3. Re: Classloading OSGi / Modules
              djchapm

              Hi, I'm fairly new to OSGI myself but think maybe I've dealt with what you're trying to do and succeeded.  You're trying to connect to an OSGI bundle from a non-OSGI bundle like a standard web-app or something right?  If so, then deployment-structure is not the way to go.  What I do for dev/compile time is add a runtime dependency on the API bundle, and remove the <type> stanza from that dependency in your pom file.  For this to work in eclipse I had to build my bundle like "mvn install -Dpackaging=jar".  I believe this is only an IDE Thing though.

               

              That gets you past runtime references without your build trying to include a copy of the bundle jar file.

               

              Then to reference the bundle at runtime you need to package up your non-OSGI web app (or whatever you are using) so that a manifest entry is added.

               

              And with 7.1.x, it has to be Exact - this caused me a lot of pain.  Here's how I package my war using build plugin configuration in pom ( Sorry about the formatting!!!) - important part is the <archive> part:

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

               

              <build

              >

               

               

               

               

              <!-- Maven will append the version to the finalName (which is the name

              given to the generated war, and hence the context root) -->

               

              <finalName>${project.artifactId}</finalName

              >

               

               

              <plugins

              >

               

               

              <!-- Compiler plugin enforces Java 1.6 compatibility and activates annotation

              processors -->

               

               

              <plugin

              >

               

               

              <artifactId>maven-compiler-plugin</artifactId

              >

               

               

              <version>2.3.1</version

              >

               

               

              <configuration

              >

               

               

              <source>1.6</source

              >

               

               

              <target>1.6</target

              >

               

               

              </configuration

              >

               

               

              </plugin

              >

               

               

              <plugin

              >

               

               

              <artifactId>maven-war-plugin</artifactId

              >

               

               

              <version>2.1.1</version

              >

               

               

              <configuration

              >

               

               

              <!-- Java EE 6 doesn't require web.xml, Maven needs to catch up! -->

               

               

              <failOnMissingWebXml>false</failOnMissingWebXml

              >

               

               

              <archive

              >

               

               

              <manifestEntries

              >

               

               

              <Dependencies>org.osgi.core,deployment.SIApi:0.1.0,deployment.SILibApi:0.1.0,org.jboss.osgi.framework</Dependencies

              >

               

               

              </manifestEntries

              >

               

               

              </archive

              >

               

               

              </configuration

              >

               

               

              </plugin

              >

              -------------

              So my bundles here are called (the ArtifactId) SIApi and SILibApi both at version 0.1.0.  You specify in manifest so the show up as "deployment.<Name:Version>". 

               

              If I bust open my war file and look at manifest I see:

              Dependencies: org.osgi.core,deployment.SIApi:0.1.0,deployment.SILibApi:0.1.0,org.jboss.osgi.framework

               

              Dan C.

              • 4. Re: Classloading OSGi / Modules
                ulrichromahn

                Dan,

                 

                your approach described above will work, with two caveats, however

                1. This only works if your SIApi and SILibApi bundles are deployed inside the deployments folder or via the admin console (CLI or Web). But, I guess, this should be the standard way anyways.
                2. With the approach above, you are simply using your OSGi bundles as libraries and do not take advantage of any OSGi features. However, as Rainer pointed out in a related post, you may not need OSGi at all and hence the question: why the trouble of building and deploying an OSGi bundle at all?
                • 5. Re: Classloading OSGi / Modules
                  werrenmi

                  Hello Ulrich, Dan

                   

                  Thanks both very much to explain your solutions!! I tried Dan's one because its more simple (sorry Ulrich )

                   

                  I hope this classloader configuration working also within a switchyrad deployment. If this the case, then i am happy

                   

                  But was i think its strange, in the AS Documentation is nowhere described thats there a difference between dependency definitions in the manifest or the "jboss-deployment-structure.xml". Because i tried the same classloader configuration in this file before and that's doesn't work.

                   

                  @ Ulrich

                  Through this dependency management i can access enough OSGi functionality for my uscase ... i can get service instances, registered by the OSGi bundle and otherwise register OSGi services and so on within the webapp (as long as i use interfaces provided by the bundle). But whats should be tested is the behavior for the case, if a webapp class instance hold a reference of a service instance that's bound to the OSGi bundle, and this service would be unregistered by the bundle, first of all when the bundle is stopped or removed.

                   

                  Regards

                  Michel

                  • 6. Re: Classloading OSGi / Modules
                    djchapm

                    Hi Uli -can you give me more information?  In my situation, and probably Michel's, we're forced to use a non-OSGI web app as the client.  I know this is a little different with AS7.3.

                     

                    So we have WebApp -> SIApi (OSGI) -- -- -- > SIImplementation (OSGI).

                     

                    I have proven that I can redeploy my SIImplementation module while the web application is up and running, and changes are automatic.  Which is point of OSGI.  So why do you think I"m not using OSGI?

                     

                    Please explain as I'd like to understand what you are saying - and definitely if you think I'm doing something wrong.

                     

                    Thanks!

                      Dan C.

                    • 7. Re: Classloading OSGi / Modules
                      werrenmi

                      Hello together

                       

                      We are now going to another approach. CDI and HornetQ. We have also JSE platform clients and HornetQ sessions are much more easier to handle then OSGi intgration.

                       

                      Thanks for your help!

                       

                      Regards

                      Michel