0 Replies Latest reply on Jun 19, 2008 1:08 PM by slawomir.wojtasiak

    Nested EJB3 modules deployment process and JBossWS.

    slawomir.wojtasiak

      Hello all,

      I hope I have chosen right forum ;)

      I'm using my own dedicated deployer which is responsible for deploying some kind or archives that are similar to plain EARs. This deployer is derived from SubDeployerSupport so I have got support for nested deployments out of the box. Deployed archives are not the EARs so nested EJB3 modules are not deployed as a part of EAR archive. There is a problem with the InvocationHandlerEJB3 which treats all EJB3 components from modules which have their parent module set (In DeploymentInfo), as a part of EAR archive and tries to add "ear" key to their ObjectNames while this key shouldn't be set because these modules are not deployed as a part of EAR but as a part of other archive (They are registered the same way as standalone EJB3 modules are.). ObjectName prepared this way is obviously not known so "Cannot find service endpoint target..." exception is thrown.

      I would like to point here that I use JBoss 4.2.2.GA and jbossws-native 3.0.1 (but jbossws-native has nothing to do with this problem.)

      It is shown in the code below:

      // org.jboss.wsf.container.jboss42.InvocationHandlerEJB3
      
      public void init(Endpoint ep)
      {
       String ejbName = ep.getShortName();
       ArchiveDeployment dep = (ArchiveDeployment)ep.getService().getDeployment();
       String nameStr = "jboss.j2ee:name=" + ejbName + ",service=EJB3,jar=" + dep.getSimpleName();
       if (dep.getParent() != null) <---- Mentioned condition (It is not DeploymentInfo but this object is based on it)
       {
       nameStr += ",ear=" + dep.getParent().getSimpleName();
       }
      
       objectName = ObjectNameFactory.create(nameStr.toString());
      
       Dispatcher dispatcher = Dispatcher.singleton;
       if (dispatcher.getRegistered(objectName.getCanonicalName()) == null)
       throw new WebServiceException("Cannot find service endpoint target: " + objectName);
      }
      

      In my opinion this condition is not enough because not every parent of EJB3 module has to be an EAR archive. See "org.jboss.ejb3.Ejb3Module" constructor and method "getDeploymentQualifiedName" of "org.jboss.ejb3.EJBContainer" to confirm it. This method returns exactly what is needed in earlier mentioned "init" method of "org.jboss.wsf.container.jboss42.InvocationHandlerEJB3".

      // org.jboss.ejb3.EJBContainer
      
      public String getDeploymentQualifiedName()
      {
       DeploymentScope ear = deployment.getEar(); <--- DeploymentScope is a determinant of EAR.
       DeploymentUnit unit = deployment.getDeploymentUnit();
       StringBuilder sb = new StringBuilder();
       if (ear != null)
       {
       sb.append("ear=");
       sb.append(ear.getShortName());
       sb.append(",");
       }
       sb.append("jar=");
       sb.append(unit.getShortName());
       sb.append(",name=");
       sb.append(getEjbName());
       return sb.toString();
      }
      

      EJB3 module is treated as a part of EAR only if Ejb3Deployment (deployment field in this case) returns an instance of DeploymentScope which is prepared by Ejb3Module for parent EAR archive. This object is available ONLY if parent archive name ends with .ear suffix or .ear/ in case of directory deployments.

      // org.jboss.ejb3.Ejb3Module
      
      public Ejb3Module(DeploymentInfo di)
      {
       DeploymentScope deploymentScope = null;
       if (di.parent != null)
       {
       if (di.parent.shortName.endsWith(".ear") || di.parent.shortName.endsWith(".ear/"))
       {
       synchronized(di.parent.context)
       {
       deploymentScope = (DeploymentScope)di.parent.context.get("EJB3_EAR_METADATA");
       if (deploymentScope == null)
       {
       deploymentScope = new JmxDeploymentScopeImpl(di.parent.shortName);
       di.parent.context.put("EJB3_EAR_METADATA", deploymentScope);
       }
       }
       }
       }
       deployment = new Ejb3JmxDeployment(di, deploymentScope);
       if (deploymentScope != null)
       {
       deploymentScope.register(deployment);
       }
       this.di = di;
      }
      

      Looking at this code one might conclude that DeploymentScope stored in parent module context indicates EAR archive, not the existence of the parent deployment itself.

      ------

      I'm pretty sure that it was not intended to work this way and that it is a bug, but I would like to see your opinions before I report a bug.

      I had to prepare quick workaround, there are modified methods:

      Class: org.jboss.wsf.container.jboss42.JAXWSDeployerHookEJB3
      Archive: jbossws-jboss42.jar from JBoss AS 4.2.2.GA
      Method: createDeployment.

       @Override
       public Deployment createDeployment(DeploymentInfo di)
       {
       ArchiveDeployment dep = newDeployment(di);
       dep.setRootFile(new URLLoaderAdapter(di.localUrl));
       dep.setRuntimeClassLoader(di.ucl);
       dep.setType(getDeploymentType());
      
       Service service = dep.getService();
      
       Ejb3ModuleMBean ejb3Module = EJBArchiveMetaDataAdapterEJB3.getEJB3Module(di.deployedObject);
       for (Object manager : ejb3Module.getContainers().values())
       {
       if (manager instanceof EJBContainer)
       {
       EJBContainer container = (EJBContainer)manager;
       if (isWebServiceBean(container))
       {
       String ejbName = container.getEjbName();
       String epBean = container.getBeanClassName();
      
       // Get qualified name of the EJB3 module. It is possible to get it directly from
       // EJBContainer in its original form so I don't see why don't use it. Are there any
       // contra arguments?
       if( dep instanceof AbstractExtensible ) {
       AbstractExtensible extensible = (AbstractExtensible)dep;
       if( !extensible.getProperties().contains( "EJB3_QUALIFIED_NAME" ) ) {
       String deploymentQualifiedName = Ejb3Module.BASE_EJB3_JMX_NAME + "," + container.getDeploymentQualifiedName();
       extensible.setProperty( "EJB3_QUALIFIED_NAME", deploymentQualifiedName );
       }
       }
      
       // Create the endpoint
       Endpoint ep = newEndpoint(epBean);
       ep.setShortName(ejbName);
       service.addEndpoint(ep);
       }
       }
       }
      
       return dep;
       }
      

      Class: org.jboss.wsf.container.jboss42.InvocationHandlerEJB3
      Archive: jbossws-jboss42.jar from JBoss AS 4.2.2.GA
      Method: init.

       public void init(Endpoint ep)
       {
       String ejbName = ep.getShortName();
       ArchiveDeployment dep = (ArchiveDeployment)ep.getService().getDeployment();
      
       // Try to get qualified name prepared earlier
       String nameStr = null;
       if( dep instanceof AbstractExtensible ) {
       nameStr = (String)((AbstractExtensible)dep).getProperty( "EJB3_QUALIFIED_NAME" );
       }
      
       if ( nameStr == null ) {
       nameStr = "jboss.j2ee:name=" + ejbName + ",service=EJB3,jar=" + dep.getSimpleName();
       if (dep.getParent() != null)
       {
       nameStr += ",ear=" + dep.getParent().getSimpleName();
       }
       }
      
       objectName = ObjectNameFactory.create(nameStr.toString());
      
       Dispatcher dispatcher = Dispatcher.singleton;
       if (dispatcher.getRegistered(objectName.getCanonicalName()) == null)
       throw new WebServiceException("Cannot find service endpoint target: " + objectName);
       }
      

      Regards,
      Sławomir Wojtasiak