1 2 Previous Next 16 Replies Latest reply on Aug 6, 2008 10:34 AM by adrian.brock

    StructureContext and candidate annotations

      https://jira.jboss.org/jira/browse/JBDEPLOY-66

      I've done the basic StructureContext stuff.

      That's leave Ales to do the JBDEPLOY-67.

      The basic idea of JBDEPLOY-67 is introduce a new mode to the candidate
      deployment visitor. Where we not only check that we recognise the context,
      but also check whether the context has one a given set of annotations.

      I'd suggest:

      1) Add a property to StructureContext, something like

      boolean candidateAnnotationScanning;
      


      2) In MockEARStructureDeployer add code to visit children in this mode
      structureContext.setCandidateAnnotationScanning(true);
      addAllChildren(structureContext);
      


      3) Add a new property to StructureDeployer
      boolean supportsCandidateAnnotations;
      

      The default should be false and avoids invoking structural deployers
      that don't understand what this means.

      4) In StructureDeployerWrapper, ignore StructureDeployers where this
      property is false when the candidateAnnotationScanning is enabled.

      5) Add support to AbstractStructureDeployer to scan for annotations
      i.e. add a
      Set<Class<? extends Annotation>> candidateAnnotations;
      

      with a helper method.

      Currently the MockEARStructureDeployer does this wrong because
      it is using the vfs resource visitor, which doesn't exist at this point - no classloader.

      6) Modify JARStructureDeployer to invoke the additional annotation scan check
      when candidateAnnotationScanning is enabled and if it fails, do a
      structureContext.removeChild(context);
      that it just created.

      This check should happen before it itself looks for subdeployments.

      It's important that it only includes the root in the scanning since
      we don't want to add a jar to an ear if it is a class in one of the manifest
      jars that has an annotation.

      This seems to me the easiest way to resolve the issue with minimal
      disruption to the logic in other structure deployers.
      i.e. You have to explicitly write and configure this behaviour on your
      structure deployer.

      If we decide we should do the same thing for wars in the future
      it should be easy to reuse the same code. As long as the helper
      code supports multiple roots in the virtual files to scan,
      i.e. WEB-INF/classes and WEB-INF/lib/*.jars

      It also externalises the annotations that we look for, so we can
      add things like JBoss's EJB3 @Service or anything new that comes along.

        • 1. Re: StructureContext and candidate annotations

          While doing this code, I noticed there was a bug in FileStructure.

          It goes to some effort to set a flag called "isFile"
          but the bug was it added the file as a subdeployment regardless
          of whether that flag was set.

          This probably doesn't cause any problems since nothing will recognise
          the file for metadata purposes and it has no classpath, but it is obviously
          stupid.

          The patch is pretty simple:

          
           // Create a context info for this file
          + if (isFile)
           context = createContext(structureContext);
           // There are no subdeployments for files
           if (trace)
           log.trace(file + " isFile: " + isFile);
           return isFile;
          


          • 2. Re: StructureContext and candidate annotations

            JBDEPLOY-66 should be 100% backwards compatible.

            I've raised JBDEPLOY-70 to remove these backwards compatiblity methods
            once JBossAS/OSGi have been update to the new api.

            • 3. Re: StructureContext and candidate annotations
              alesj

               

              "adrian@jboss.org" wrote:

              4) In StructureDeployerWrapper, ignore StructureDeployers where this
              property is false when the candidateAnnotationScanning is enabled.

              Ignore how / what?

              Probably not in this way:
               public boolean determineStructure(StructureContext context) throws DeploymentException
               {
               if (context == null)
               throw new IllegalArgumentException("Null context");
              
               if (supportsCandidateAnnotations() == false)
               return false;
              

              since we then don't even do basic recognition.

              • 4. Re: StructureContext and candidate annotations
                alesj

                We probably need to keep the found information somewhere.

                e.g. on StructureContext

                Set<Class<? extends Annotation>> foundAnnotations;
                

                So we can then later on set ear module's type:
                 // Create subdeployments for the ear modules
                 for (EarModule mod : modules)
                 {
                 String fileName = mod.getFileName();
                 if (fileName != null && (fileName = fileName.trim()).length() > 0)
                 {
                 try
                 {
                 VirtualFile module = file.getChild(fileName);
                 if (module == null)
                 {
                 throw new RuntimeException(fileName
                 + " module listed in application.xml does not exist within .ear "
                 + file.getName());
                 }
                 // Ask the deployers to analyze this
                 if (structureContext.determineChildStructure(module) == false)
                 {
                 throw new RuntimeException(fileName
                 + " module listed in application.xml is not a recognized deployment, .ear: "
                 + file.getName());
                 }
                // SET MODULE's TYPE HERE
                 }
                 catch (IOException e)
                 {
                 throw new RuntimeException(fileName
                 + " module listed in application.xml does not exist within .ear "
                 + file.getName(), e);
                 }
                 }
                 }
                


                • 5. Re: StructureContext and candidate annotations

                   

                  "alesj" wrote:
                  "adrian@jboss.org" wrote:

                  4) In StructureDeployerWrapper, ignore StructureDeployers where this
                  property is false when the candidateAnnotationScanning is enabled.

                  Ignore how / what?

                  Probably not in this way:
                   public boolean determineStructure(StructureContext context) throws DeploymentException
                   {
                   if (context == null)
                   throw new IllegalArgumentException("Null context");
                  
                   if (supportsCandidateAnnotations() == false)
                   return false;
                  

                  since we then don't even do basic recognition.


                  You don't want to do basic recognition. If the structure doesn't support
                  annotation scanning then you don't want to add to the ear.
                  e.g. a -service.xml shouldn't be added to the ear if it isn't listed in
                  application/jboss-app.xml


                  • 6. Re: StructureContext and candidate annotations
                    alesj

                     

                    "adrian@jboss.org" wrote:

                    You don't want to do basic recognition. If the structure doesn't support
                    annotation scanning then you don't want to add to the ear.
                    e.g. a -service.xml shouldn't be added to the ear if it isn't listed in
                    application/jboss-app.xml

                    OK, makes sense:
                     if (context.isCandidateAnnotationScanning() && deployer.supportsCandidateAnnotations() == false)
                     return false;
                    


                    • 7. Re: StructureContext and candidate annotations

                       

                      "alesj" wrote:
                      "adrian@jboss.org" wrote:

                      You don't want to do basic recognition. If the structure doesn't support
                      annotation scanning then you don't want to add to the ear.
                      e.g. a -service.xml shouldn't be added to the ear if it isn't listed in
                      application/jboss-app.xml

                      OK, makes sense:
                       if (context.isCandidateAnnotationScanning() && deployer.supportsCandidateAnnotations() == false)
                       return false;
                      


                      deployer.isSupportsCandidateAnnotations()

                      It needs to be a property on the structure deployer so you can
                      turn off this behaviour if you don't want to speed things up.

                      • 8. Re: StructureContext and candidate annotations
                        alesj

                        I've added this to AbstractStructureDeployer:

                         /**
                         * Create deployment resource loader.
                         *
                         * @param root the deployment root
                         * @return new deployment resource loader
                         */
                         protected abstract DeploymentResourceLoader createDeploymentResourceLoader(VirtualFile root);
                        
                         /**
                         * Create annotation environment.
                         *
                         * @param classLoader the classloader
                         * @return new annotation environment
                         */
                         protected abstract AnnotationEnvironment createAnnotationEnvironment(ClassLoader classLoader);
                        
                         /**
                         * Check for candidate annotations.
                         *
                         * @param context the structure context
                         * @param roots the roots to check
                         */
                         protected void checkCandidateAnnotations(StructureContext context, VirtualFile... roots)
                         {
                         if (roots == null || roots.length == 0)
                         throw new IllegalArgumentException("Null or empty roots");
                        
                         if (candidateAnnotations == null || candidateAnnotations.isEmpty())
                         return;
                        
                         for(VirtualFile root : roots)
                         {
                         DeploymentResourceLoader loader = createDeploymentResourceLoader(root);
                         ClassLoader cl = new DeploymentResourceClassLoader(loader);
                         AnnotationEnvironment env = createAnnotationEnvironment(cl);
                         for (Class<? extends Annotation> annotationClass : candidateAnnotations)
                         {
                         if (env.hasClassAnnotatedWith(annotationClass))
                         {
                         context.addFoundAnnotation(annotationClass);
                         }
                         }
                         }
                         }
                        

                        Where MockEARStructureDepolyer then checks in
                        Module's StructureContext if we have any matching annotation.

                        OK?

                        • 9. Re: StructureContext and candidate annotations

                           

                          "alesj" wrote:

                          Where MockEARStructureDepolyer then checks in
                          Module's StructureContext if we have any matching annotation.
                          OK?


                          I've told you before. I'm not going to say
                          yes/no to snippets of source posted in the forums.
                          I'm not javac. ;-)

                          If you're asking me to verify implementation details then I might as well write it myself
                          at least I can then run it through the compiler and write unit tests.

                          If you have an explicit question, e.g. choice of two possible implementations that's ok.

                          Having said that I don't see why the getters for DeploymentResourceLoader and AnnotatedEnvironment are abstract. There should be default implementations
                          in the abstract class.


                          • 10. Re: StructureContext and candidate annotations
                            alesj

                             

                            "adrian@jboss.org" wrote:

                            Having said that I don't see why the getters for DeploymentResourceLoader and AnnotatedEnvironment are abstract. There should be default implementations in the abstract class.

                            Since the owner class is in deployers-vfs-spi, which doesn't know about the real impls, e.g. DefaultAnnotationEnvironment and VFSDeploymentResourceLoaderImpl.
                            For this I've created additional AbstractVFSStructureDeployer, which has these impls as default.

                            • 11. Re: StructureContext and candidate annotations
                              alesj

                               

                              "alesj" wrote:

                              Where MockEARStructureDepolyer then checks in
                              Module's StructureContext if we have any matching annotation.

                              In order to do this, I need some way to get a child StructureContext from parent StructureContext:
                              e.g.
                               private Integer determineType(StructureContext context, VirtualFile archive) throws Exception
                               {
                               boolean recognised = context.determineChildStructure(archive);
                               if (recognised)
                               {
                               StructureContext subContext = context.getChildContext(archive);
                               Set<Class<? extends Annotation>> foundAnnotations = subContext.getFoundAnnotations();
                               if (foundAnnotations.contains(Stateless.class))
                               return J2eeModuleMetaData.EJB;
                               }
                              


                              Where is the best way/place to store this info?
                              While creating new StructureContext?
                               public StructureContext(VirtualFile root, VirtualFile parent, VirtualFile file, StructureMetaData metaData, VFSStructuralDeployers deployers, StructureContext parentContext)
                               {
                               ...
                               this.parentContext = parentContext;
                               if (parentContext != null)
                               {
                               // ADD CHILD INFO HERE
                               }
                               }
                              


                              • 12. Re: StructureContext and candidate annotations

                                I don't really understand what you are trying to do,
                                but children are described by the ContextInfos of the StructureMetaData.

                                • 13. Re: StructureContext and candidate annotations
                                  alesj

                                   

                                  "adrian@jboss.org" wrote:
                                  I don't really understand what you are trying to do,
                                  but children are described by the ContextInfos of the StructureMetaData.

                                  I need a place to store found annotations.
                                  Or, a way to get additional information about deployment,
                                  e.g. it's an EJB deployment since there is some class annotated with @Stateless.

                                  In the case of EAR I need to check all sub deployments that are not explicitly described in application.xml.
                                  First they need to be recognized by some strucuture deployer, in our case currently only JARStructure can recognize it (combination of StructureContext.isCandidateAnnotationScanning() && Deployer.isSupportsCandidateAnnotations() == false).
                                  After it is recognized, I need to add additional type information to the EarModule metadata (EJB, Client, Connector, Service, ...).

                                  So, I was thinking of keeping found annotation classes in the StructureContext instance.
                                  But in EAR's case, I have access to top level structure context, where I actually need to check sub context's found annotations.

                                  I don't see how ContextInfo can help me there.
                                  Unless I keep this annotation lookup info there.


                                  • 14. Re: StructureContext and candidate annotations

                                     

                                    "alesj" wrote:
                                    "adrian@jboss.org" wrote:
                                    I don't really understand what you are trying to do,
                                    but children are described by the ContextInfos of the StructureMetaData.

                                    I need a place to store found annotations.
                                    Or, a way to get additional information about deployment,
                                    e.g. it's an EJB deployment since there is some class annotated with @Stateless.

                                    In the case of EAR I need to check all sub deployments that are not explicitly described in application.xml.
                                    First they need to be recognized by some strucuture deployer, in our case currently only JARStructure can recognize it (combination of StructureContext.isCandidateAnnotationScanning() && Deployer.isSupportsCandidateAnnotations() == false).
                                    After it is recognized, I need to add additional type information to the EarModule metadata (EJB, Client, Connector, Service, ...).

                                    So, I was thinking of keeping found annotation classes in the StructureContext instance.
                                    But in EAR's case, I have access to top level structure context, where I actually need to check sub context's found annotations.

                                    I don't see how ContextInfo can help me there.
                                    Unless I keep this annotation lookup info there.


                                    Looking at the MockEARStructureDeployer, isn't the issue
                                    that you need to be able to do some extra processing once you've
                                    found the annotations?

                                    i.e. not only does the EARStructure need to setCandidateAnnotationScanning(true)
                                    it also needs to be able to pass a callback such as

                                    public EARCandidateAnnotationsCallback implement CandidateAnnotationsCallback
                                    {
                                     List<EARModule> modules;
                                    
                                     public EARCandidateAnnotationsCallback(List<EARModule> modules)
                                     {
                                     this.moduleMetaData = moduleMetaData;
                                     }
                                    
                                     public void annotationScanningCallback(StructureContext context, AnnotationEnvironment env)
                                     {
                                     EARModule module = determineModuleFromAnnotations(context, env);
                                     modules.add(module);
                                     }
                                    }
                                    


                                    Then JARStructure does something like:

                                    if (annotationScanningEnabled && deploymentRecognised && annotationPresent)
                                    {
                                     addChild();
                                     context.runAnnotationScanningCallbacks(context, env);
                                    }
                                    


                                    I'm not suggesting this is the exact way you should do it,
                                    since I haven't looked at the problem domain, but
                                    this kind of "visitor pattern" is usually preferable to trying
                                    to hack some kind of state into stateless designs
                                    which always gets very messy and hacky.

                                    Especailly in cases like this where the JARStructure should not
                                    need to know anything about EARs.
                                    The EARStructure may not even be deployed in some environments!
                                    There could be other top level structures that also want this behaviour in future.

                                    P.S. In case you haven't already spotted it (its not mentioned in my
                                    original blurb), you also need some logic to avoid visiting candidates
                                    and doing annotation scannning on modules that are already known
                                    by the EARStructure from the xml.

                                    1 2 Previous Next