XDoclet not generating Local interface
crowse Aug 2, 2004 8:29 AMHi,
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 { }