6 Replies Latest reply on Jul 5, 2007 11:13 AM by adrian.brock

    AOP ClassLoader/Scoping needs thoroughly revising

      There is a fundamentally bad assumption going on with AOP usage of classloading
      and scoping. The problem is exemplified by this code in ScopedClassLoaderDomain
      (but it is a more general issue that I will explain as I go on):

       private LoaderRepository getAspectRepository(Object aspect)
       {
       ClassLoader cl = aspect.getClass().getClassLoader();
       ClassLoader parent = cl;
       while (parent != null)
       {
       if (parent instanceof RepositoryClassLoader)
       {
       return ((RepositoryClassLoader)parent).getLoaderRepository();
       }
       parent = parent.getParent();
       }
       return null;
       }
      


      The above code is like using Class.forName() instead of the context classloader.
      It assumes co-location (either aspect and code or aspect and config or config and code)
      which just isn't true in all cases.

      When I say co-location I'm really talking about what AOP calls Domains
      although the way this currently implemented, it is closely related to classloading.

      In fact, the way AOP tries to guess what is going on and create domains
      itself based on "hardwired rules" is clearly wrong.

      The only reason it works is because of the "big ball mud" classloading model
      currently used by JBoss or the standalone classpath specified on the command line.

      It is also exemplified by the conflicting (overloaded) usage of Domains:
      1) As a scoped configuration repository for the user
      2) As a way to scope classloading where a class is deployed multiple times
      in different deployments
      3) Like (1) but done by us, e.g. the ejb3 confurgation domain

      Before I explain what I would like to see, let me explain some of the use cases
      which are either not supported or at least difficult to do.

        • 1. Re: AOP ClassLoader/Scoping needs thoroughly revising

          Before I continue with the use cases, two caveats.

          1) I going to be loose with what I explain, and try to explain it from a user's point
          of view. e.g. If talk about intercepting database calls on Oracle, the user
          is unlikely to know that Oracle is the implementing class and instead
          specify join points on the jdbc interfaces. In the following I'll probably say both
          but it is really the same thing, hopefully you can follow this loose speak
          without pulling me up on what is done technically.

          2) Some of what I describe as not possible can be done, but it is either
          not easy or not obvious how to do it. e.g. In some scoping situations
          you can make it work by using the MC's aop-mc-int and use proxies to get
          "instance level adivce stacks" but I'm not assuming that is what will be or we
          want to be used in all cases?

          • 2. Re: AOP ClassLoader/Scoping needs thoroughly revising

            Use case: ClassLoader (really class location) versus deployment scoping

            The requirement here is that all deployment situations that should work
            will work as expected.

            Typically there are three items in the mix
            (I'll explain the AOP terminology as well for those readers that are not familiar with it)

            1) The class being "modified" (I'll call this the "advised class"), e.g. this would
            be Oracle's Connection class
            2) The advice (or interceptor if you like) which is the what "modification" you want
            to make to the class
            3) The configuration, i.e. what are the advices, to which classes do they apply and where in that class they are made (called joinpoints in AOP)

            The use case comes when these are not all in the same deployment or you want
            to do different things in different deployments.

            e.g. Use Case 1: Different advices in different deployments
            Suppose I have Oracle's jdbc jar deployed such that everybody can see the classes
            and I want to advise those classes in different ways in different applications

            Currently, the advices are linked to the class through what JBoss AOP calls
            the advisor. That is every joinpoint has a list of advices (could be no list if the
            joinpoint is not advised).

            None of those lists can be changed at the deployment level.

            There is a notion perJVM, perClass, perInstance, but this is more about
            how instances of the advice you get, not what the advice is or does.

            e.g. Use Case 2: Different configuration in different deployments
            This is similar to use case 1, but the advices are classes that are also globally available.
            But the configuration (the -aop.xml) is different in the different deployments.

            e.g. Use Case 3: Different advised classes in different deployments
            Suppose I deploy different Oracle jars in different applications
            and then specify global/local advices and global/local configuration.
            This is the kind of thing that will work "out-of-the-box" because
            there are multiple versions of the classes to add advisors to.

            e.g. Use Case X: Other variants
            There are other variants some of which may or may not make sense,
            but those that do are probably unlikely to work just by looking at the classloader
            of the advised class or advice to determine what configuration to use.

            • 3. Re: AOP ClassLoader/Scoping needs thoroughly revising

              Use case: "subsystem" aop config

              We already have a concrete version of this in EJB3
              where the EJB3 containers have a specific domain for the aop config.
              But it is a hardwired implementation with no general support for this feature
              to be used by other subsystems

              Use Case 1: Adding advices for the JCA stack

              I already prototyped a version of the JCA stacks through AOP.
              e.g. You can define what behaviour to add in front of the jdbc api
              for the jdbc outbound rar
              but again this is a hardwired use of domains.

              The key thing here is that we want the JCA advices (pooling, security, transaction
              enlistment) only for the outbound jdbc rar. We don't want these advices
              to apply to none JCA usage of jdbc.

              Use Case 2: Different JCA stacks at the deployment level

              The JCA stacks could actually be deployment specific themselves.
              e.g. there is a local-jdbc.rar and an xa-jdbc.rar
              which have different requirements

              Use Case 3: Differfent JCA stacks at the instance level

              The JCA stacks could even change for a specific data-source deployment in a -ds.xml
              That is for this datasource only I want to add to or change the advice stack.

              • 4. Re: AOP ClassLoader/Scoping needs thoroughly revising

                So what I'm getting at is that just determining the aop advices from the
                classloader of the advice or the advised class is not enough.

                What is really required, is for aop to use some notion of scope so it knows
                what server/subsystem/deployment/class/instance it is working with.

                I'm not asking for aop to implement anything related to scope
                (it already exists in fact see below), but what we need is a mechanism
                where the correct scope is used in each case.

                An integration api (to replace AOPScopedClassLoaderHelper) might be something as simple as:

                public interface DomainContextFactory
                {
                 // Get the domain from the current context (e.g. a thread local)
                 Domain getDomain();
                
                 // Get the domain for a specific scope
                 // Not sure if you really need this but you have a getTopLevelClassLoader()
                 // in the old api - not sure what is for?
                 Domain getDomain(Scope scope);
                }
                


                You might have guessed from the signature that I'm suggesting using
                the MetaDataRepository as the implementation of this api inside JBoss.

                The idea being that a Domain can be added to different scopes
                and populated by the AOPDeployer(s).

                e.g. the default scope (what is currently now the AspectManager)
                would be something like (pseudo code - not the real api for simplicity)
                MetaData md = MetaDataRepository.getScope(ScopeLevel.JVM, "Instance");
                Domain domain = rep.getMetaData(Domain.class);
                


                The first method in the proposed api would be implemented as
                (assuming the callers have setup the correct scope context)
                MetaData md = MetaDataStack.peek();
                Domain domain = rep.getMetaData(Domain.class);
                


                • 5. Re: AOP ClassLoader/Scoping needs thoroughly revising

                  Of course, I still need to integrate the MetaDataRepository into the deployers
                  such that each DeploymentUnit or Component has its own MetaData scope.

                  i.e. each deployment will have a unique MutableMetaData in a scope
                  determined by the relevant deployer that AOP can populate.

                  That is number 4 on my list of things to do, so it is not going to get done
                  straight away. :-)

                  • 6. Re: AOP ClassLoader/Scoping needs thoroughly revising

                    The issue as it appears to me in the AOP config is that it is generally storing
                    things at the class(loader) level.

                    This is a reasonable strategy to avoid memory leaks. Once the class(loader)
                    is GCed the WeakReference is removed.

                    But in general this leads to the wrong configuration or wrong advices getting
                    used. What is required is a different strategy where *logically* the advice stacks (and
                    other info?) are at the scope/class level.

                    Of course, we should still need to optimize away duplicates where there
                    is no override at more finer grained scopes. If it just took the simplistic approach
                    then you have an advice stack per instance which wouldn't scale at all. :-)

                    I'm not sure what the best way to implement this is efficently.
                    You obviously don't to be doing a scope access on every method/field call
                    to see whether there is an override for that scope.
                    And in fact, that might lead to wrong semantics where an object from
                    one application is passed to another application.
                    It could suddenly start using the wrong advices.

                    That is why I emphasised "logically" above.

                    It seems to me that the best way to do this would be like MC instance level
                    aop.

                    That is if the config/advices/class are all in the same scope/domain
                    then we just need a normal Advisor.

                    If however we are going to override a class's configuration in a child scope/domain
                    (maybe even the class is not advised in its own scope) then we use the
                    proxy stuff like the MC work.

                    Finally, the other advantage of using Scopes when they are integrated into the
                    deployers is that there is a pretty trivial point during the undeploy when
                    you know the information can be released and what information needs to be
                    released.