1 2 3 Previous Next 36 Replies Latest reply on Sep 13, 2011 1:05 PM by lmcdasi Go to original post
      • 15. Re: META-INF/services for module extensions - question/issue
        dmlloyd

        Dan Sirbu wrote:

         

        Hi Brian,

         

           I have taken a break from the subject and after giving multiple taughts I still think there is a "flaw". Here is my view based on my current understanding:

         

           I can have, let's say, two kind of modules: one that implements the module ifc thus an extension && one that does not. Let's name : ModuleExtension the one that has defined an interface towards the : org.jboss.as.controller.Extension ifc; and let's name Module the one that does not have such a definition.

          What I understand is that the ModuleExtension by design should not have any other service defined - which is OK. And if it does it is discarded - which again is OK.

        That's not really true.  Neither JBoss Modules nor JBoss AS will actively browse modules for services as this would be a significant performance drain to little advantage.  So services are never "discarded", they're simply ignored until someone asks for them.

         

         

        Dan Sirbu wrote:

         

          What I also understand is that the Module by design can have a service defined. Now the service defined into Module is available to any war, rar, etc... file that is deployed and have somehow a dependecy defined towards that Module (see prev dicussions on how one can achieve that). And the war file, for example, uses the 'regular' Service.load instead of using the jboss-module one - which is what one would expect.

          So far so good.

        I see a couple items of confusion here.  First any module which provides an AS extension must be in the static module repository, because extensions are initialized during AS boot and there are no deployments at this early stage.  Second, the ServiceActivator interface (which is what I assume you're referring to as having a "service defined") is only read by deployment descriptors, so any ServiceActivators in a static modue will be ignored.  However, an extension module can define services through other APIs so this is not really a limitation.

         

         

        Dan Sirbu wrote:

         

          Now, let's say that ModuleExtension needs Module for whaterver reason. Then one would go into "module.xml" of ModuleExtension and add a dependency towards that Module. Then, what I would expect as behaviour is that the Module service should be available to ModuleExtension via the 'regular' Service.load and not via the jboss-module service loader !

        Okay here's another point of confusion.  The JBoss Modules service loader is the "regular" ServiceLoader, just wrapped up in a helper which provides the module's class loader for you.  This point of confusion seems to be masking the real problem.  If you want your extension module to "see" the service provided by its module dependency, you simply add 'services="import"' to the dependency element for that dependency.  If you want other modules which import the extension module to "see" the services provided by that dependency, then instead use 'services="export"'.  That's it; this will make it work whether you're using Module.loadService() or ServiceLoader.

         

         

        Dan Sirbu wrote:

         

        Hi Brian,

         

        Since Module is not defined as a jboss-module extension, then the ModuleExtension should be able to have access to the Module service interface via the 'regular' Service.load and that because, to me, the Module should be in the classpath of the ModuleExtension one such a dependency has been created. There is no real need to go towards jboss-module service loader - at least I do not see one.

        As I have said, the two are one and the same.

         

         

        Dan Sirbu wrote:

         

          If a war file, for example, would need access to ModuleExtension it would add a dependecy towards it. The dependency should ensure that the objects defined with the ModuleExtension become visible to the war file including the ones from the Module meaning that the classpath of the ModuleExtension would be made available to the war. At least that is I think a dependency should do.

        This is only done when the service is explicitly declared like above.  If we did not filter these by default, then every module would see tons and tons of extra services which may or may not actually be desired and might actually be completely wrong.  However we do allow services to be shared across dependencies already as I've described, which neatly solves the problem.

        • 16. Re: META-INF/services for module extensions - question/issue
          lmcdasi

          OK - though debugging shows different behaviour. Anyhow, ain't gona argue.

           

          The point is that I have used 'services=import' and it still did not work as you described ! I will try again by using 7.0.1 release and get back to you.

           

          BR,

          Dan S.

          • 17. Re: META-INF/services for module extensions - question/issue
            brian.stansberry

            Here's my guess at to what is going on. If this doesn't describe what you're doing, then never mind.

             

            You have a class ModuleExtensionClass that's in your ModuleExtension module. And another class ServiceAPIClass that's either in ModuleExtension or Module.

             

            In ModuleExtensionClass.initialize(ExtensionContext) you are calling

             

            ServiceLoader.load(ServiceAPIClass.class)

             

            If that call is failing with a CNFE, it's a sign that the module dependencies aren't set up right. But my theory is that's not what's happening.

             

            If I'm correct so far, does using this call work?

             

            ServiceLoader.load(ServiceAPIClass.class, ModuleExtensionClass.class.getClassLoader())

             

            The single arg variant of ServiceLoader.load is the same as calling ServiceLoader.load(Class c, Thread.currentThread().getContextClassLoader()); In AS 7.0.0/7.0.1 when Extension.initialize is called the TCCL is null, so you're not going to get the correct classloader.

             

            We have changed this in master, so in 7.0.2 when Extension.initialize is called, the TCCL will the defining classloader of the Extension impl (i.e. ModuleExtensionClass.class.getClassLoader()). So, if my theory is right you should find this works in a current build of master but not in 7.0.1.

            • 18. Re: META-INF/services for module extensions - question/issue
              brian.stansberry

              Brian Stansberry wrote:

               

              And another class ServiceAPIClass that's either in ModuleExtension or Module.

               

              Actually, it would have to be in Module or it wouldn't be visible to the class that implements it.

              • 19. Re: META-INF/services for module extensions - question/issue
                lmcdasi

                The point is the following, the java.util.ServiceLoader is the common used and portable way of accessing services accross any java platform !

                We do aggree on that I hope !

                 

                Thus, my point is the following one: if I want to have a ModuleExtension that needs to implement a service then I do not have any issue that I  need to use the ServiceLoader from jboss modules ! That I understand very well, it is a jboss-module way of doing things.

                 

                The issue to me is that if the ModuleExtension needs a service from another package that was never intended to be an extention, thus a Module only, then when I want to  access a service from that Module I should NOT need to use the jboss-module ServiceLoader API !

                I should be able to use the java.util.ServiceLoader as does any WAR/RAR or other type of app deployed as long as the dependency is set properly!

                 

                My point is that in 7.0.0 that does NOT work ! Unless I am doing stupind things around - which is always a possibility - but I doubt since I am not the only one in my team to have the same experience ! I used all kind of setups, including the service="import" if I am not wrong !

                 

                And I think, from a design point of view, what I want to do should be correct and supported !

                 

                So, since so far I used 7.0.0 give me some time to use 7.0.1.

                 

                ServiceLoader.load(ServiceAPIClass.class) - fails;

                 

                module.loadService(ServiceAPIClass.class) - works where module is the Module object based on the identifier. No matter the dependecy.

                 

                I also tryed between others : ServiceLoader.load(ServiceAPIClass.class, TCCLSingletonService.class.getClassLoader().getParent()) - fails

                 

                I'll try what you mentioned. Give me some time. I'll try to attach the eclipse projects next time with all setup if I can.

                 

                BR,

                Dan S.

                • 20. Re: META-INF/services for module extensions - question/issue
                  brian.stansberry

                  Yes, we completely agree that the java.util.ServiceLoader API is the correct one to use. You shouldn't have to use any jboss-modules API.

                   

                  The difference between this case and an EAR/RAR/WAR relates to the reliance on the thread context class loader. In an EE application,  what the TCCL should be is clearly mandated in the EE spec. There is a contract the application code can rely on. In the case of an Extension there was no such contract. In the absence of a contract, the TCCL could be anything. In that kind of situation it's always better to use the 2 arg variant of java.util.ServiceLoader.load().

                   

                  I apologize if this issue has caused your team frustration. During development of AS7 we didn't have cases of anyone using Extension in this way, so how the TCCL was set did not show up as an issue. Now that we see that extension developers expect the TCCL to be set differently than we were doing it, we've changed the AS to set the TCCL to the Extension implementation's classloader. I'll change the Extension javadoc to state that, which will make this an official contract that can be relied upon.

                  • 21. Re: META-INF/services for module extensions - question/issue
                    dmlloyd

                    Dan Sirbu wrote:

                     

                    The issue to me is that if the ModuleExtension needs a service from another package that was never intended to be an extention, thus a Module only, then when I want to  access a service from that Module I should NOT need to use the jboss-module ServiceLoader API !

                    I should be able to use the java.util.ServiceLoader as does any WAR/RAR or other type of app deployed as long as the dependency is set properly!

                    I just want to make it clear that you do NOT need to use the jboss modules service loader API.  This has never been required, nor will it ever be required.  This API exists as a convenience only.

                     

                    As Brian said, you should always be using the two-argument form of java.util.ServiceLoader#load() and provide your own class loader with it (I don't know what you're trying to accomplish with the getParent() but it's a safe bet that it's not the right thing to do).  Usually this is just "getClass().getClassLoader()", though you'll have to use a class literal in the static method case.  Relying on the thread context class loader is never a good idea under any circumstances.  If you rely on TCCL by using the one-argument form, you're setting yourself up for failure at some point, either here and now or in the future.  If you use the two-argument form, your program will always work as long as your class loader has the proper dependencies.

                    • 22. Re: META-INF/services for module extensions - question/issue
                      lmcdasi

                      Hi,

                       

                         The good news is that we are converging in ideas .  No frustration - yet

                       

                         The bad news is that somehow I do not get what I think I should get . And what from design point of view sounds correct to me.

                       

                         My example is a lot more complicated than I what I explained here. I do have a ModuleExtension that depends on a Module#1, then Module#1 dependes on Module#2 and few others for example, and Module#1, Module#2 do have exposed services.

                         If I have to 'customize' ModuleExtension to kistart Module#1 - then again I do not have an issue since it is jboss module specific. BUT and from here on we diverge in opinions .... looks like

                       

                        Module#1 does also a ServiceLoader.load(ServiceAPIFromModule#2.class) - no extra argument here when the ModuleExtension executes the ServiceAPIFromModule#1. And that does not work neither - for same above issues.

                       

                         Now - if you ask me to add a second argument to ServiceLoader in Module#1 - then there is a no go - because Module#1 may be a 3rd party product that I cannot change, as example. Thus the ServiceLoader.load with one argument is the only option that can be available.

                       

                         At the same time, Module#1 code should not be impacted by the jboss-module/as7 architecture - one same simple reason: portability.

                       

                         Any existing jar file can be always be viewed as a Module - but not as an extension - which is ok !

                         Thus as a Module, if it's using ServiceLoader with one argument it should work with no code changes ! Otherwise you impleciltely are telling me that the exiting jar file is no longer portable when using it from ModuleExtension ! And this makes jboss-modules not to be flexible and imposes extra constrains that, to me, do not look to be required.

                         Think about that jar file can be used as a Module from a WAr, RAR and why not from a ModuleExtension ?!?

                       

                         Again, once I am in the Module#1 code I should be able to do a ServiceLoader.load - with only one argument as any war file does when it is deployed - if the module.xml has set a dependecy on a module and if services="import" has been set in the ModuleExtension as in the war.

                       

                        So far, I am not getting that ! And again, even if it looks stuborn, I think from design point of view I should !

                       

                      BR,

                      Dan S.

                      • 23. Re: META-INF/services for module extensions - question/issue
                        jason.greene

                        To apply some more color, we have introduced a TCCL contract (What Brian was reffering to) in upstream master (which will be released in 7.0.2).

                         

                        This will make it much easier to deal with as you mention legacy code, and also JDK APIs which just were not designed for a modular world.

                         

                        As of 7.0.2:

                        • The TCCL during any MSC Service will be the defining classloader
                        • The TCCL during any management operation (includes extensions) will be the defining CL

                         

                        So in a nutshell single arg ServiceLoader will work in 7.0.2. Although maybe you want to try our local master before that?

                        • 24. Re: META-INF/services for module extensions - question/issue
                          lmcdasi

                          Hi Jason,

                           

                             I think I can give it a try - it will be slower since I am not familiar in building the full jboss as7 release and the other steps. But I can give it a try.

                           

                          BR,

                          Dan S.

                          • 25. Re: META-INF/services for module extensions - question/issue
                            jason.greene

                            Dan Sirbu wrote:

                             

                            Hi Jason,

                             

                               I think I can give it a try - it will be slower since I am not familiar in building the full jboss as7 release and the other steps. But I can give it a try.

                             

                            BR,

                            Dan S.

                            FYI we have a latest binary build you can always grab here:

                            http://hudson.jboss.org/jenkins/view/JBoss%20AS/job/JBoss-AS-7.0.x/lastSuccessfulBuild/artifact/build/target/jboss-7.0.x.zip

                            • 26. Re: META-INF/services for module extensions - question/issue
                              brian.stansberry

                              We're definitely getting on the same page, and I'm glad we're discussing in detail; it's worthwhile for me, hopefully for you, hopefully for others who may read this.

                               

                              Jason's already pointed out that 7.0.2 should solve the practical issue. We've made the caller setting the TCCL a part of the contract of the Extension interface.

                               

                              A lot of the rest of what I've said and what David has said is more oriented toward best practices and such. One thing the AS7 team has encountered a lot in the course of developing AS 7 is frameworks (some of which we helped write sometime in the past) that make assumptions about what the TCCL will be in the absence of a contract. So we're always trying to spread the word about being careful about making that assumption.

                               

                              Dan Sirbu wrote:

                               

                                Now - if you ask me to add a second argument to ServiceLoader in Module#1 - then there is a no go - because Module#1 may be a 3rd party product that I cannot change, as example. Thus the ServiceLoader.load with one argument is the only option that can be available.

                               

                              Sounds like you're in the same boat we are in. You have unknown code that you are calling into that is making assumptions about the TCCL. In this case before calling into it you can set the TCCL to the correct classloader, hopefully one defined in the ServiceAPIClass contract. That could very well be the defining classloader for Module#1's impl of the service interface.

                               

                              The classloader for the ModuleExtension module, which in 7.0.2 will be the TCCL, may not have visibility to classes in Module#2. That's the nature of modular classloading -- the intent is to provide proper isolation, and not to expose everything to everybody. So if you think Module#1 is going to be relying on the TCCL having visibility to everything Module#1 depends on, then you should set the TCCL to a classloader that should have that visibility. Most likely that is Module#1's classloader.

                              • 27. Re: META-INF/services for module extensions - question/issue
                                lmcdasi

                                To Jason: many thanks for the link - that spares me for extra trouble

                                 

                                To Brian: yes - there are for sure other people that are in the 'dark' as me and any open discussion is always productive.

                                 

                                I'll go ahead with Jason link and since I will be 'mean'  - I will create one ModuleExtension and three Modules #1, #2, #3 with the following setup:

                                 

                                     ModuleExtension loads --> ServiceLoadApiFromModule#1 --> executes the impl from Module#1

                                 

                                     ServiceLoadApiFromModule#1 impl loads --> ServiceLoadApiFromModule#2 --> executes the impl from Module #2 see next (so Module#1 needs to have TCCL visibility on Module#2 )

                                 

                                     ServiceLoadApiFromModule#2 --> ServiceLoadApiFromModule#3 (so Module#2 needs to have TCCL visibility on Module#3) --> executes the impl from Module #3 --> test stops

                                 

                                     In a genric way, once in a Module, the Module should have visibility on all other Modules that were added as dependecies in the ModuleExtension, to cover more complex stuff.

                                 

                                ModuleExtension -> ServiceLoadApiFromModule#1 loader can happen with 2 arguments since it comes from ModuleExtension (it can use even the jboss-module service loader) but the remaining:

                                 

                                ServiceLoadApiFromModule#2,#3 - shall happen only with 1 argument since it is comming from Module and shall use the java.util.ServiceLoader.load().

                                 

                                Should the below achievable via module.xml setup as discussed ? If not, what would be the a code snapshot on classpath settings.

                                 

                                Brian Stansberry wrote:

                                 

                                The classloader for the ModuleExtension module, which in 7.0.2 will be the TCCL, may not have visibility to classes in Module#2. That's the nature of modular classloading -- the intent is to provide proper isolation, and not to expose everything to everybody. So if you think Module#1 is going to be relying on the TCCL having visibility to everything Module#1 depends on, then you should set the TCCL to a classloader that should have that visibility. Most likely that is Module#1's classloader.

                                 

                                Is this sound correct to you ?

                                 

                                BR,

                                Dan S.

                                • 28. Re: META-INF/services for module extensions - question/issue
                                  brian.stansberry

                                  So the scenario is

                                   

                                  ME -> M1 -> M2 -> M3

                                   

                                  with "->" meaning <dependencies><module name="X"/>... in the left side module's module.xml and no 'export="true"' attribute. So ME can only see M1's classes, M1 can only see M2, M2 can only see M3.

                                   

                                  If M2 uses the single param java.util.ServiceLoader.load() variant and doesn't do anything to change the TCCL, this setup will not work. In 7.0.2 when M2 is executing the TCCL will have visibility to the classes in ME and M1 (if ME doesn't change the TCCL from what the AS passes in) or to the classes in M1 and M2 (if ME changes the TCCL to M1's classloader.) Either way, M3 classes are not visible.

                                   

                                  Options:

                                   

                                  1) If ME, M1, M2, M3 are really a unit, package ME.jar, M1.jar, M2.jar, M3.jar in a single module.

                                   

                                  2) In ME's module.xml's dependencies section, add

                                   

                                  <module name="M1"/>

                                  <module name=M2"/>

                                  <module name="M3"/>

                                   

                                  The TCCL passed in by AS 7.0.2.+ will be able to see all relevant classes.

                                   

                                  3) In M2's module.xml have <module name="M3" export="true"/> and in M1's module.xml have <module name="M2" export="true"/>

                                   

                                  Again, the TCCL passed in by the AS will be able to see all relevant classes. If some other module depends on M1, the M2 and M3 classes will be visible to that module as well. Which may be a good thing or a bad thing. If it's bad, solution 2) is better.

                                   

                                  4) If M1, M2, M3 are really a unit, package M1.jar, M2.jar, M3.jar in a single module.

                                   

                                  5) In M1's module.xml's dependencies section, add

                                   

                                  <module name=M2"/>

                                  <module name="M3"/>

                                   

                                  Then in ME, change the TCCL to M1's classloader before calling into M1. When M1 calls into M2, the TCCL will be one that can see M3's classes.

                                   

                                  6) In M2's module.xml have <module name="M3" export="true"/>. M3's classes will now be visible to M1. Then in ME, change the TCCL to M1's classloader before calling into M1. When M1 calls into M2, the TCCL will be one that can see M3's classes. If some other module depends on M2, the M3 classes will be visible to that module as well. Which may be a good thing or a bad thing.

                                   

                                  7) Package M2.jar and M3.jar in a single module, M2+. Then in ME, change the TCCL to M1's classloader before calling into M1. When M1 calls into M2+, the TCCL will be one that can see M3.jar's classes. If some other module depends on M2+, the M3.jar classes will be visible to that module as well. Which may be a good thing or a bad thing.

                                   

                                  Dan Sirbu wrote:

                                   

                                       In a genric way, once in a Module, the Module should have visibility on all other Modules that were added as dependecies in the ModuleExtension, to cover more complex stuff.

                                   

                                  This can be done in a variety of ways, as described above. What can't be done is having a module's dependencies exposed without the consent of the module. A module's dependencies are treated as internal details unless the export="true" attribute is added.

                                  • 29. Re: META-INF/services for module extensions - question/issue
                                    lmcdasi

                                    Sorry Brian, but it does not look to me that is working. I did not go too far: ME-->M1 does not work so ....

                                     

                                    ME: module.xml has dependencies:

                                          <module name="com.module.first"/>        M1
                                          <module name="com.module.second"/>   M2
                                          <module name="com.module.third"/>       M3

                                     

                                    Then in the TCCL class:

                                     

                                     

                                     

                                     

                                     

                                     

                                    import com.module.first.services.TestServicesIfc;

                                     

                                    import com.module.first.services.TestServicesImpl;

                                     

                                    .....

                                     

                                    module = Module.getCallerModuleLoader().loadModule(myModule.getIdentifier());

                                     

                                    ServiceLoader<?> serviceLoader = module.loadService(TestServicesIfc.

                                    class);                      <--- SUCCESS since it is using the jboss-modules api

                                    Iterator<?> serviceIter = serviceLoader.iterator();

                                    if

                                    (serviceIter.hasNext()) {

                                     

                                    log.info("TestServicesIfc -------> Found services class"

                                    );

                                    ......

                                    ServiceLoader<TestServicesIfc> myService = ServiceLoader.load(TestServicesIfc.class);        <-- FAILS

                                    Iterator<TestServicesIfc> iter = myService.iterator();

                                     

                                    }

                                    else {

                                     

                                    log.info("TestServicesIfc -------> NOT Second Found services class");

                                    }

                                     

                                     

                                    10:44:57,216 INFO  [org.jboss.as.jmx.JMXConnectorService] (MSC service thread 1-2) Starting remote JMX connector
                                    10:44:57,217 INFO  [org.jboss.as.remoting] (MSC service thread 1-3) Listening on /127.0.0.1:9999
                                    10:44:59,268 INFO  [org.jboss.as.connector] (MSC service thread 1-3) Starting JCA Subsystem (JBoss IronJacamar 1.0.3.Fin
                                    al)
                                    10:44:59,705 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-2) Bound data source [java:jbos
                                    s/datasources/ExampleDS]
                                    10:45:36,561 INFO  [com.modext.dans.services.TCCLSingletonService] (MSC service thread 1-4) TestServicesIfc -------> Fou
                                    nd services class
                                    10:45:38,274 INFO  [com.module.first.services.TestServicesImpl] (MSC service thread 1-4) HELLO
                                    10:45:50,258 INFO  [com.modext.dans.services.TCCLSingletonService] (MSC service thread 1-4) TestServicesIfc -------> NOT
                                    Second Found services class
                                    10:45:55,941 INFO  [org.jboss.as.deployment] (MSC service thread 1-3) Started FileSystemDeploymentService for directory
                                    c:\jboss-as-7.1.0.Alpha1-SNAPSHOT\standalone\deployments
                                    10:45:55,979 INFO  [org.jboss.as] (Controller Boot Thread) JBoss AS 7.1.0.Alpha1-SNAPSHOT "Lightning" started in 69825ms
                                    - Started 94 of 149 services (55 services are passive or on-demand)
                                    10:45:58,719 INFO  [org.jboss.as.osgi] (MSC service thread 1-3) Stopping OSGi Framework
                                    10:45:58,737 INFO  [org.jboss.as.logging] Restored bootstrap log handlers
                                    10:45:58,792 INFO  [com.arjuna.ats.jbossatx] ARJUNA32018: Destroying TransactionManagerService
                                    10:45:58,794 INFO  [com.arjuna.ats.jbossatx] ARJUNA32014: Stopping transaction recovery manager
                                    10:45:58,799 INFO  [org.jboss.as] JBoss AS 7.1.0.Alpha1-SNAPSHOT "Lightning" stopped in 96ms

                                    if (iter.hasNext()) {

                                     

                                    log.info("TestServicesIfc -------> Second trial: Found services class");