3 Replies Latest reply on Aug 3, 2004 5:33 PM by crowse

    XDoclet not generating Local interface

    crowse

      Hi,
      I am having a problem using XDoclet to geterate methods into the local interface. The remote interface is working fine.

      Either I am missing something, or there are serious performance gains to be made using XDoclet and EJB.

      The generated Local interface looks like a useless extension of the HOME interface!!

      On a live bean, deployed in JBoss, about 99% of the time is spent in the getXXX() and setXXX() methods of remote bean. This is acceptible when I access a few beaans individually, but useless if I want to findall() 20000 beans of 50Kb each and do some work on them.

      I want to be able to delegate this task to the EE container so everything runs locally, and get rid of the RMI calls.

      Using a value object also doesnt help since the data must be streamed between the EE container and the web (tomcat) containers.



      My thinking is to have a number of Stateless Session beans, that run in the EE container and have the form

       public String method() {
       // Use JNDI to find Local Business bean
       StringBuffer sb = new StringBuffer();
       Collection c = bean.findAll();
       // Iterate over collection
       while (it.hasNext()) {
       // get Bean or its attributes
       // using REMOTE about 99% of the time is spent HERE
       // do something
       // log to sb;
       // perhaps create some other beans
       // can also take substantial time in setXXX) or ejbCreate() methods
       }
       return sb.toString();
      }
      
      


      I have included the whole source , which is rather long, so that I dont miss something important.


      Any ideas. or help will be much appreciated.

      ----------Here is the EJBsource code ---------------------------------------
      package natis.ejb;
      import java.io.UnsupportedEncodingException;
      
      import java.rmi.*;
      
      import javax.naming.InitialContext;
      import javax.naming.NamingException;
      
      import javax.ejb.*;
      
      /**
       *
       * @ejb.bean
       * name="TestBean"
       * jndi-name="ejb/TestBeanRemote"
       * local-jndi-name="ejb/TestBeanLocal"
       * view-type="both"
       * type="CMP"
       * cmp-version="2.x"
       * primkey-field="id"
       * local-business-interface="natis.interfaces.TestBeanLocalHome"
       * business-interface="natis.interfaces.TestBeanRemoteHome"
       *
       * @ejb.home
       * local-class="natis.interfaces.TestBeanLocalHome"
       * remote-class="natis.interfaces.TestBeanRemoteHome"
       * @ejb.interface
       * local-class="natis.interfaces.TestBeanLocal"
       * remote-class="natis.interfaces.TestBeanRemote"
       *
       * @ejb.finder
       * signature="Collection findAll()"
       * role-name="Administrator"
       * transaction-type="NotSupported"
       * unchecked="true"
       *
       * @ejb.persistence
       * read-time-out="10000"
       * fetch-size="20"
       * table-name="TestBean_001"
       *
       * @ejb.pk
       * generate="true"
       * unchecked="true"
       *
       * @jboss.ejb-external-ref-jndi
       * jndi-name="/UUIDKeyGeneratorFactory"
       * ref-name="comp:env/keygenerator"
       *
       *
       * @jboss.create-table "${jboss.create.table}"
       * @jboss.remove-table "${jboss.remove.table}"
       * @jboss.tuned-updates "${jboss.tuned.updates}"
       * @jboss.read-only "${jboss.read.only}"
       * @jboss.read-ahead strategy="on-load"
       * page-size="50"
       * eager load group = "TestBeanIndex"
       *
       * @jboss.persistence
       * datasource="java:/DefaultDS"
       * datasource-mapping="Hypersonic SQL"
       * create-table="true"
       * remove-table-"false"
       * fetch-size="10"
       *
       * @ejb.security-identity
       * run-as="sa"
       *
       * @ejb.value-object
       * match="*"
       *
       * @struts.form
       * name="Entry"
       * include-all="true"
       *
       * @struts.action
       * name="TestSaveAction"
       * path="/natis/action/saveTest"
       * scope="request"
       * roles="*"
       *
       * @version $Revision: 1.17 $
       * @author <a href="mailto:chris@forthtree.co.za@forthtree.co.za">Chris@forth.co.za</a>
       */
      public abstract class TestBean implements EntityBean {
      
       EntityContext entityContext;
      
       /**
       * EJBCREATE
       * @return pk primary key set to null
       *
       * @ejb.create-method
       */
       public String ejbCreate() throws CreateException {
       return null;
       }
       public void ejbPostCreate() throws CreateException {
       }
      
       /**
       * Id of this TestBean.
       *
       * @ejb.pk-field
       * class="java.lang.String"
       * @ejb.persistent-field
       * @ejb.interface-method view-type="both"
       *
       * @ejb.persistence column-name="TestBean_id"
       * @ejb.permission
       * role-name="Administrator"
       *
       * @jboss.unknown-pk
       * class="java.lang.String"
       * readonly="true"
       * column-name="TestBean_id"
       * sql-type="char(32)"
       * //jboss.auto-increment
       *
       * @jonas.cmp-field-jdbc-mapping
       * field-name="id"
       * jdbc-field-name="TestBean_id"
       */
       public abstract String getId();
      
       /**
       * No interface method for setId(..). See page 130 of the EJB 2.0 specification:
       * "Once the primary key for an entity bean has been set, the Bean Provider must
       * not attempt to change it by use of set accessor methods on the primary key
       * cmp-fields. The Bean provider should therefore not expose the set accessor
       * methods for the primary key cmp-fields in the component interface of the
       * entity bean.". A work around would be to remove and then an re-create the bean.
       */
       protected abstract void setId(String id);
      
      
       /**
       * @ejb.permission
       * role-name="Administrator"
       * @ejb.transaction
       * type="Supports"
       * @ejb.value-object
       * exclude="false"
       * match="*"
       * @ejb.persistent-field
       * @ejb.persistence
       * column-name="mfData"
       * sql-type="text(500)"
       * @ejb.interface-method
       * view-type="both"
       *
       * @struts.form-field
       * form-name="Entry"
       *
       * @jonas.cmp-field-jdbc-mapping
       * field-name="mfData"
       * jdbc-field-name="mfData"
       */
      
       public abstract String getData();
       /**
       * @ejb.permission
       * role-name="Administrator"
       * @ejb.transaction
       * type="Supports"
       * @ejb.value-object
       * exclude="false"
       * match="*"
       * @ejb.persistent-field
       * @ejb.interface-method
       * view-type="both"
       *
       * @struts.form-field
       * form-name="Entry"
       *
       */
       public abstract void setData(String mfData);
      
       /** * @ejb.interface-method
       * view-type="local"
      
       */
       public String toIx() {
       return getId() + "|" + getData();
       }
      
      
      
      
      
       /**
       * Gets the EntityContext. To be used by classes extending this.
       * @return the EntityContext of the EJB
       */
       protected EntityContext getEntityContext() {
       return entityContext;
       }
      
       /** Required to implement EntityBean. Sets the EntityContext. */
       public void setEntityContext(EntityContext entityContext) throws EJBException {
       this.entityContext = entityContext;
       }
      
       /** Required to implement EntityBean. Sets the EntityContext to null. */
       public void unsetEntityContext() throws EJBException {
       entityContext = null;
       }
      
       /** Required to implement EntityBean. Not implemented. */
       public void ejbActivate() throws EJBException { }
      
       /** Required to implement EntityBean. Not implemented. */
       public void ejbPassivate() throws EJBException { }
      
       /** Required to implement EntityBean. Not implemented. */
       public void ejbLoad() throws EJBException { }
      
       /** Required to implement EntityBean. Not implemented. */
       public void ejbStore() throws EJBException { }
      
       /** Required to implement EntityBean. Not implemented. */
       public void ejbRemove() throws RemoveException, EJBException { }
      
      
      
      
      }



      ---------- Here is the EJBDOCLET tag --------------------------------------

      This should (I believe) generate the local and remote interfaces, and include all methods marked 'local' and 'both' in the local interface.

      An EJB should then be available within the J2EE container where I can use these methods locally.

      <target depends="prepare" name="ejbdoclet">
      
       <ejbdoclet
       destdir="${project.gen-src.dir}"
       mergedir="${project.merge.dir}"
       excludedtags="@version,@author,@todo"
       ejbspec="2.0"
       force="${project.xdoclet.force}"
       verbose="true"
       >
      
       <fileset dir="${project.java.dir}">
       <include name="${project}/ssb/*Bean.java"/>
       <include name="${project}/ejb/*Bean.java"/>
       <include name="${project}/ejb/*BeanMS.java"/>
       <include name="${project}/ejb/${project}*.java"/>
       <include name="${project}/ejb/*Error.java"/>
       <exclude name="${project}/ejb/Base*.java"/>
       </fileset>
      
       <fileset dir="${project.tmp.dir}">
       <include name="${project}/**/*Bean.java"/>
       <include name="${project}/ejb/*BeanMS.java"/>
       <include name="${project}/**/*Error.java"/>
       </fileset>
      
       <packageSubstitution packages="ejb" substituteWith="interfaces"/>
       <packageSubstitution packages="absbeans" substituteWith="interfaces"/>
      
       <session/>
       <remoteinterface/>
       <localinterface/>
       <homeinterface/>
       <localhomeinterface/>
       <valueobject/>
       <entitypk/>
       <entitycmp/>
       <entitybmp/>
       <dao>
       <packageSubstitution packages="ejb" substituteWith="dao"/>
       </dao>
       <utilobject cacheHomes="true" includeGUID="true"/>
       <deploymentdescriptor
       description="${project} Deployment"
       destdir="${project.target.dir}/META-INF"
       mergedir="${project.merge.dir}"
       validatexml="true">
       <configParam name="clientjar" value="${project}Client.jar"/>
       </deploymentdescriptor>
      
      
       <jboss
       version="3.2"
       unauthenticatedPrincipal="nobody"
       xmlencoding="UTF-8"
       destdir="${project.target.dir}/META-INF"
       validatexml="true"
       preferredrelationmapping="relation-table"
       />
      
       <strutsform/>
      
       <castormapping destdir="${project.target.dir}/META-INF" validatexml="false"/>
       </ejbdoclet>
       </target>
      


      ---------- Here is the generated Remote interface ------------------
      All is OK from a generation perspective.

      However , when I use the bean I find that a huge proportion of the time is used in the get and set methods. This is presumably time in the RMI since the time taken is irrespective of the amount of data being shifted.

      Using a value object also does not help. I presume because a huge amount of data must be serialised and transmitted, first from the database (Microsoft SQL Server2000) to the EJB, then via the value bean to the SErvlet, then back again.


      /*
       * Generated by XDoclet - Do not edit!
       */
      package natis.interfaces;
      
      /**
       * Remote interface for TestBean.
       * @xdoclet-generated at 2-08-04
       * @copyright The Forthtree
       * @author Chris Rowse
       * @version ${version}
       */
      public interface TestBeanRemote
       extends javax.ejb.EJBObject
      {
       /**
       * Id of this TestBean.
       */
       public java.lang.String getId( )
       throws java.rmi.RemoteException;
      
       public java.lang.String getData( )
       throws java.rmi.RemoteException;
      
       public void setData( java.lang.String mfData )
       throws java.rmi.RemoteException;
      
      }
      
      
      

      ---------- Here is the generated LOCAL interface
      This bit I really dont understand.

      Firstly, why does it extend its own Home?
      Second, Where are my business methods?

      Is theere another way to get to the EJB from a Session bean running in the same J2EE container?

      /*
       * Generated by XDoclet - Do not edit!
       */
      package natis.interfaces;
      
      /**
       * Local interface for TestBean.
       * @xdoclet-generated at 2-08-04
       * @copyright The Forthtree
       * @author Chris Rowse
       * @version ${version}
       */
      public interface TestBeanLocal
       extends javax.ejb.EJBLocalObject, natis.interfaces.TestBeanLocalHome
      {
      
      }
      


        • 1. Re: XDoclet not generating Local interface
          pique

          As a first step, include attribute
          "extends" and "local-extends"
          in tag @ejb.home and @ejb.interface
          like this
          * @ejb.home
          * extends="javax.ejb.EJBHome"
          * local-extends=....

          • 2. Re: XDoclet not generating Local interface
            darranl

            From reading the XDoclet documentation you need to remove the following lines from the @ejb.bean section: -

            * local-business-interface="natis.interfaces.TestBeanLocalHome"
             * business-interface="natis.interfaces.TestBeanRemoteHome"


            You have instructed XDoclet to use the local home interface as the local interface.



            • 3. Re: XDoclet not generating Local interface
              crowse

              Hi pique and darranl

              Thanks for the info. You may both give me a virtual kick in the pants.

              DarranI, in fact, these lines should point to the business (i.e. Remote and ((local??)) interfaces, not the homes.

              A further problem is that if I point the business interface to Remote, everything is OK.

              But if I try to point the local-business-interface to my Local interface (NOT LOcal Home), I get an error message (Circular inheritance). I removed the local-business interface tag entirely, and all is well.

              MAny many thanks

              Chris