7 Replies Latest reply on May 5, 2009 9:30 AM by alesj

    Conflict between ejb3 deployer and custom GenericAnnotationD

      Using JBoss AS 5.0.1 GA

      I am trying to write custom deployer that will scan classes for annotation during POST_CLASSLOADING phase, save the found classes into metadata and then use that to perform some extra steps during REAL phase (similar to WS stuff but for QMF protocol).

      I have a test enviroment with sample EJB3 app and a custom deployer. However when I start JBoss I get the following exception:


      10:59:20,213 ERROR [AbstractKernelController] Error installing to Real: name=vfszip:/home/gmostizk/java/jboss-5.0.1.GA/server/default/deploy/vdcServer-0.0.1-SNAPSHOT.ear/ state=PreReal mode=Manual requiredState=Real
      org.jboss.deployers.spi.DeploymentException: Error deploying vdcServer-0.0.1-SNAPSHOT.ear: Container jboss.j2ee:ear=vdcServer-0.0.1-SNAPSHOT.ear,jar=vdcServer-0.0.1-SNAPSHOT.ear,name=ConfigManagerBean,service=EJB3 failed to resolve persistence unit null
       at org.jboss.ejb3.deployers.Ejb3Deployer.deploy(Ejb3Deployer.java:201)
       at org.jboss.ejb3.deployers.Ejb3Deployer.deploy(Ejb3Deployer.java:103)
       at org.jboss.deployers.vfs.spi.deployer.AbstractVFSRealDeployer.internalDeploy(AbstractVFSRealDeployer.java:45)
       at org.jboss.deployers.spi.deployer.helpers.AbstractRealDeployer.deploy(AbstractRealDeployer.java:50)
       at org.jboss.deployers.plugins.deployers.DeployerWrapper.deploy(DeployerWrapper.java:171)
       at org.jboss.deployers.plugins.deployers.DeployersImpl.doDeploy(DeployersImpl.java:1439)
       at org.jboss.deployers.plugins.deployers.DeployersImpl.doInstallParentFirst(DeployersImpl.java:1157)
       at org.jboss.deployers.plugins.deployers.DeployersImpl.install(DeployersImpl.java:1098)
       at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:348)
       at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:1598)
       at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:934)
       at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:1062)
       at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:984)
       at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:822)
       at org.jboss.dependency.plugins.AbstractController.change(AbstractController.java:553)
       at org.jboss.deployers.plugins.deployers.DeployersImpl.process(DeployersImpl.java:781)
       at org.jboss.deployers.plugins.main.MainDeployerImpl.process(MainDeployerImpl.java:698)
       at org.jboss.system.server.profileservice.ProfileServiceBootstrap.loadProfile(ProfileServiceBootstrap.java:304)
       at org.jboss.system.server.profileservice.ProfileServiceBootstrap.start(ProfileServiceBootstrap.java:205)
       at org.jboss.bootstrap.AbstractServerImpl.start(AbstractServerImpl.java:405)
       at org.jboss.Main.boot(Main.java:209)
       at org.jboss.Main$1.run(Main.java:547)
       at java.lang.Thread.run(Thread.java:619)
      Caused by: java.lang.IllegalArgumentException: Container jboss.j2ee:ear=vdcServer-0.0.1-SNAPSHOT.ear,jar=vdcServer-0.0.1-SNAPSHOT.ear,name=ConfigManagerBean,service=EJB3 failed to resolve persistence unit null
       at org.jboss.injection.PersistenceUnitHandler.addPUDependency(PersistenceUnitHandler.java:135)
       at org.jboss.injection.PersistenceContextHandler.loadXml(PersistenceContextHandler.java:76)
       at org.jboss.ejb3.EJBContainer.processMetadata(EJBContainer.java:588)
       at org.jboss.ejb3.Ejb3Deployment.processEJBContainerMetadata(Ejb3Deployment.java:415)
       at org.jboss.ejb3.Ejb3Deployment.start(Ejb3Deployment.java:523)
       at org.jboss.ejb3.deployers.Ejb3Deployer.deploy(Ejb3Deployer.java:194)
       ... 22 more
      Caused by: java.lang.IllegalArgumentException: Can't find a persistence unit named 'null' in AbstractVFSDeploymentContext@5463591{vfszip:/home/gmostizk/java/jboss-5.0.1.GA/server/default/deploy/vdcServer-0.0.1-SNAPSHOT.ear/}
       at org.jboss.jpa.resolvers.BasePersistenceUnitDependencyResolver.resolvePersistenceUnitSupplier(BasePersistenceUnitDependencyResolver.java:107)
       at org.jboss.ejb3.Ejb3Deployment.resolvePersistenceUnitSupplier(Ejb3Deployment.java:720)
       at org.jboss.ejb3.EJBContainer.resolvePersistenceUnitSupplier(EJBContainer.java:1428)
       at org.jboss.injection.PersistenceUnitHandler.addPUDependency(PersistenceUnitHandler.java:130)
       ... 27 more
      


      The exception thrown during deployment of the sample application.

      My custom GenericAnnotationDeployer:
      public class QmfTypeAnnotationDeployer extends GenericAnnotationDeployer {
       @Override
       protected void visitModule(DeploymentUnit deploymentUnit, Module module, GenericAnnotationResourceVisitor genericAnnotationResourceVisitor) {
       super.visitModule(deploymentUnit, module, genericAnnotationResourceVisitor);
      
       AnnotationEnvironment annotationEnvironment = genericAnnotationResourceVisitor.getEnv();
       Set<Element<QMFSeeAlso,Class<?>>> qmfSeeAlsoAnnotations = annotationEnvironment.classIsAnnotatedWith(QMFSeeAlso.class);
      
       QmfRegisteredClassesMetaData metaData = new QmfRegisteredClassesMetaData();
      
       // create metadata for each class we need to register
       for (Element<QMFSeeAlso, Class<?>> qmfSeeAlsoAnnotation : qmfSeeAlsoAnnotations) {
      
       // add the root class
       Class<?> annotatedClass = qmfSeeAlsoAnnotation.getAnnotatedElement();
       metaData.add(annotatedClass);
      
       // add all the children
       Class[] subClasses = qmfSeeAlsoAnnotation.getAnnotation().value();
       for (Class subClass : subClasses) {
       metaData.add(subClass);
       }
       }
      
       // attach metadata to unit
       deploymentUnit.addAttachment(QmfRegisteredClassesMetaData.class, metaData);
       }
      }
      


      REAL phase deployer:
       private void registerQmfTypes(DeploymentUnit unit) {
       QmfRegisteredClassesMetaData metadata = unit.getAttachment(QmfRegisteredClassesMetaData.class);
       if(metadata==null) return;
      
       for (Class classToRegister : metadata.getAnnotatedClasses()) {
       log.info("Exposing "+classToRegister.getSimpleName()+" as QMFType");
       qmfAgent.registerClass(classToRegister);
       }
       }
      


      jboss-beans.xml
       <bean name="QmfServiceDeployerEJB" class="org.jboss.qmf.core.deployers.QmfServiceDeployerEJB">
       <depends>EJB2xDeployer</depends>
       <depends>Ejb3Deployer</depends>
      
       <property name="qmfAgent">
       <inject bean="QmfAgent" />
       </property>
       </bean>
      
      
       <bean name="QmfTypeAnnotationDeployer" class="org.jboss.qmf.core.deployers.QmfTypeAnnotationDeployer"/>
      


      If I comment out the last line from XML everything loads perfectly (without my custom work) so it seems there is some potential conflict between GenericAnnotationDeployer and normal EJB3 deployment process.

      I am stuck on this so I would appreciate your help.

      Thanks,
      Gregory

        • 1. Re: Conflict between ejb3 deployer and custom GenericAnnotat
          alesj

          Some simple observations.

          You shouldn't extend GenericAnnotationDeployer
          (as it means then that you're doing the visit twice - our impl + your deployer).
          You should simply pull out the AnnotationEnvironment from the attachments
          (in your deployer) and do your custom check.

          Although I would be surprised if this was the cause.
          I'll see if I can think of something more ...

          • 2. Re: Conflict between ejb3 deployer and custom GenericAnnotat

            I was able to do a workaround solution after stepping through all the deployment code yesterday. It works now but I would appreciate your comments on whether I did it right and also for the benefit of whoever gets stuck with it in the future.

            I added the following to the QmfTypeAnnotationDeployer class:

             public QmfTypeAnnotationDeployer() {
             log.info("Initalizing "+this.getClass().getSimpleName());
             addInput(JBossMetaData.class);
             setOutputs(new HashSet<String>());
             setOutput(QmfRegisteredClassesMetaData.class);
             }
            


            What this does is it makes sure that my custom annotation deployer runs after all normal EJB3 annotation scanners. The way I understand it is that the problem was that by default GenericAnnotationDeployer sets its output as AnnotationEnviroment and this creates a conflict between it and some other deployer from EJB3 package which also outputs AnnotationEnviroment but slightly changed. Since the previous version of my custom deployer didnt have any inputs it was running first and basically giving wrong AnnotationEnviroment to the rest of EJB3 deployers - which (after some more processing) would generate the exception I have experienced.

            If my understanding is correct the fix should be easy - just change the output of EJB3 deployer to something like setOutput('internal.'+AnnotationEnviroment.class.getSimpleName()) which will make sure that both AnnotationEnviroments can co-exist happily :)

            • 3. Re: Conflict between ejb3 deployer and custom GenericAnnotat
              alesj

               

              "gregory.mostizky@gmail.com" wrote:

              If my understanding is correct the fix should be easy - just change the output of EJB3 deployer to something like setOutput('internal.'+AnnotationEnviroment.class.getSimpleName()) which will make sure that both AnnotationEnviroments can co-exist happily :)

              EJB3 deployer can only use AnnotationEnvironment (AE),
              hence is shouldn't declare itself as an AE provider / output.

              And like I said, having two AE's is wrong.
              e.g. it might load the classes too soon, when it's not intended - aop, ...

              The proper fix is to use the existing one
              and just build the info on top of it in a new deployer.

              • 4. Re: Conflict between ejb3 deployer and custom GenericAnnotat

                When I tried your previous suggestion it didn't work with the same exception. The only way to workaround that was to override GenericAnnotationDeployer output(). Also, when I disabled my custom deployer entirely, I was still able to get AnnotationEnviroment during REAL phase. That's why I think there is another deployer somewhere that attaches AE to deployment unit and that is what causing the conflict.

                • 5. Re: Conflict between ejb3 deployer and custom GenericAnnotat
                  alesj

                   

                  "gregory.mostizky@gmail.com" wrote:
                  That's why I think there is another deployer somewhere that attaches AE to deployment unit and that is what causing the conflict.

                  Afaik there is only one.
                  It's the
                   <bean name="GenScanDeployer" class="org.jboss.deployers.vfs.plugins.annotations.FilteredAnnotationEnvironmentDeployer">
                  

                  in deployers/metadata-deploy-jboss-beans.xml.

                  But I could be wrong, as I don't know all deployers. :-)

                  • 6. Re: Conflict between ejb3 deployer and custom GenericAnnotat

                    I think it makes sense that the one you posted is the one causing the conflict.
                    Just from quick look at its source code, it seems that it applies additional filtering and scoping on top of GenericAnnotationDeployer which is consistent with what I saw when I disabled my custom deployer - I could get AnnotationEnviroment but some classes were missing (i.e. filtered out).

                    So the root of the problem seems to be that it is possible to use both of them and each one will try to overwrite AnnotationEnviroment attachment with slightly different data resulting in unpredictable behavior. Since both attachments are valid the fix IMO should be just assigning unique "attachment ids" to each one instead of both sharing AnnotationEnviroment.class

                    • 7. Re: Conflict between ejb3 deployer and custom GenericAnnotat
                      alesj

                       

                      "gregory.mostizky@gmail.com" wrote:
                      I think it makes sense that the one you posted is the one causing the conflict.
                      Just from quick look at its source code, it seems that it applies additional filtering and scoping on top of GenericAnnotationDeployer which is consistent with what I saw when I disabled my custom deployer - I could get AnnotationEnviroment but some classes were missing (i.e. filtered out).

                      Ah, ic.
                      Yes, we do some filtering chain, in order not to scan too much by default.
                      But you can change that easily.

                      "gregory.mostizky@gmail.com" wrote:

                      So the root of the problem seems to be that it is possible to use both of them and each one will try to overwrite AnnotationEnviroment attachment with slightly different data resulting in unpredictable behavior. Since both attachments are valid the fix IMO should be just assigning unique "attachment ids" to each one instead of both sharing AnnotationEnviroment.class

                      I still think we should force usage of a single AE.
                      As encouraging multiple AE is not the way to go,
                      performance wise in the first place.

                      I'll need to change this filtering anyway,
                      as I should do scanning no matter if JBossMD says it's complete.
                      e.g. I should scan @Entity anyway