Hi guys
The last few days I was playing around with Ricards doclet stuff.
I think we should create a little framework for it to make it more
powerful. (I believe the idea of using doclets is great, because it
gives us an type safe object model with additional meta data.)
As additional capabilities I could imagine:
- smart PKGeneration - if PK is simple Java type (eg Integer),
we dont need a extra PK
- SourcefileValidator - check the given files before generating
anything
- TestClientGenerator - maby a little complex but should be possible
for simple test runs (create, find, set/get, ...)
With view of CMP2.0 I tried to automatically generate the
<relationships> tags of the ejb-jar.xml file. Therefor I added some
doclet tags and wrote a class RelationAssembler that tries to figure
out the relations between the given classes. For easy use I put it into
the same package I hope that is OK.
You only have to put the source file to the other files of the package
and insert into EJBDoclet in the ejbxml-method between the
</assembly-descriptor> and </ejb-jar> lines (~line 287) the following
code line:
new RelationAssembler (root, System.out).dump (out, 3);
and recompile it...
Any comments are welcome
Bye Daniel
---8<-- The Java Source Code ------------------------------------------
package com.dreambean.ejbdoclet;
import java.io.*;
import java.util.*;
import com.sun.javadoc.*;
/**
Used on a collection of CMP bean source files following the tag convention, this
tool
generates <code>RelationAssembler.Relation</code> objects for each relation
between the
given objects. <br>
The Getter and Setter (actually only the Getters) for the Container Managed
Relationship
fields must use the following tags:
<dl>
<dt><code><b>@cmr-field</b></code></dt><dd>for marking this method as a getter or
setter for a
CMR field
</dd><dt><code><b>@cmr-related-name</b></code></dt><dd> if the
returntype/parameter of this method
is a Collection, this tag needs to be set to specify the type (classname) of
collected (related) objects.
</dd><dt><code><b>@cmr-related-field-name</b></code></dt><dd>in case of
bidirectional navigation
the name of the corresponding field in the related class must be given here
</dd>
<dl>
used tags:
entity-cmp,
dependent-object,
cmr-field,
cmr-related-name,
cmr-related-field-name,
dependent-name,
ejb-name,
@author <A href="mailto:[EMAIL PROTECTED]">Daniel Schulze</A>
@version 0.1
*/
public class RelationAssembler
{
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
RootDoc root;
PrintStream log;
Hashtable relations;
// Constructors --------------------------------------------------
/** Creates a new RelationAssembler object
* @param _root the RootDoc of the Doclet run
* @param _log a PrintStream for logging */
public RelationAssembler (RootDoc _root, PrintStream _log)
{
root = _root;
log = _log;
start ();
}
// public --------------------------------------------------------
/** starts assembling the relationships of the given repository */
public void start ()
{
relations = new Hashtable ();
Iterator entities = getCMPEntities ();
while (entities.hasNext ())
processClass ((ClassDoc) entities.next ());
Iterator dependents = getDependentObjects ();
while (dependents.hasNext ())
processClass ((ClassDoc) dependents.next ());
}
/** Dumps the generated Relations into the given PrintStream with the given indent
depth
* @param _out PrintStream to dump into (the ejb-jar.xml file)
* @param _indent the indent depth */
public void dump (PrintStream _out, int _indent)
{
String i = "";
while (_indent-- > 0) i += " ";
_out.println(i+"<relationships>");
Iterator it = getRelations ();
while (it.hasNext ())
{
Relation r = (Relation)it.next ();
_out.println(i+i+"<ejb-relation>");
_out.println(i+i+i+"<ejb-relation-name>"+r.name+"</ejb-relation-name>");
_out.println(i+i+i+"<ejb-relationship-role>");
_out.println(i+i+i+i+"<ejb-relationship-role-name>"+r.role1.name+"</ejb-relationship-role-name>");
_out.println(i+i+i+i+"<multiplicity>"+r.role1.multiplicity+"</multiplicity>");
_out.println(i+i+i+i+"<role-source>");
_out.println(i+i+i+i+i+"<"+r.role1.sourceType+">"+r.role1.sourceName+"</"+r.role1.sourceType+">");
_out.println(i+i+i+i+"</role-source>");
if (!"".equals (r.role1.cmrFieldName))
{
_out.println(i+i+i+i+"<cmr-field>");
_out.println(i+i+i+i+i+"<cmr-field-name>"+r.role1.cmrFieldName+"<cmr-field-name>");
if (!"".equals (r.role1.cmrFieldType))
_out.println(i+i+i+i+i+"<cmr-field-type>"+r.role1.cmrFieldType+"<cmr-field-type>");
_out.println(i+i+i+i+"</cmr-field>");
}
_out.println(i+i+i+"</ejb-relationship-role>");
_out.println(i+i+i+"<ejb-relationship-role>");
_out.println(i+i+i+i+"<ejb-relationship-role-name>"+r.role2.name+"</ejb-relationship-role-name>");
_out.println(i+i+i+i+"<multiplicity>"+r.role2.multiplicity+"</multiplicity>");
_out.println(i+i+i+i+"<role-source>");
_out.println(i+i+i+i+i+"<"+r.role2.sourceType+">"+r.role2.sourceName+"</"+r.role2.sourceType+">");
_out.println(i+i+i+i+"</role-source>");
if (!"".equals (r.role2.cmrFieldName))
{
_out.println(i+i+i+i+"<cmr-field>");
_out.println(i+i+i+i+i+"<cmr-field-name>"+r.role2.cmrFieldName+"<cmr-field-name>");
if (!"".equals (r.role2.cmrFieldType))
_out.println(i+i+i+i+i+"<cmr-field-type>"+r.role2.cmrFieldType+"<cmr-field-type>");
_out.println(i+i+i+i+"</cmr-field>");
}
_out.println(i+i+i+"</ejb-relationship-role>");
_out.println(i+i+"</ejb-relation>");
}
_out.println(i+"</relationships>");
}
/** Returns the created Relation objects */
public Iterator getRelations ()
{
return relations.values ().iterator ();
}
// protected -----------------------------------------------------
/** Processes the given class.
* @param the class to process */
protected void processClass (ClassDoc _clazz)
{
ClassDoc current = _clazz;
Iterator cmrFields = getCMRFields (current);
while (cmrFields.hasNext ())
{
MethodDoc field = (MethodDoc) cmrFields.next ();
try {
log.print (" processing "+current.name ()+"."+getFieldName
(field)+"...");
// if we dont find the related class
// it must be a relationship with a bean outside
// so we cant assemble it
// if the bean shall be included to the jar, then the
// develeoper must add it to the scaned classes...
ClassDoc related = getRelatedClass (field);
// names for this relation
String name1 = current.name () + "." + getFieldName (field);
String name2 = related.name ();
Relation relation = new Relation ();
// check from this fields view
String rt = field.returnType ().qualifiedTypeName ();
if (rt.equals ("java.util.Collection") ||
rt.equals ("java.util.Set"))
{
// to many
relation.role2.multiplicity = "many";
relation.role1.cmrFieldType = rt;
} else
{
// to one
relation.role2.multiplicity = "one";
relation.role1.cmrFieldType = "";
}
relation.role1.cmrFieldName = getFieldName (field);
String rf = getRelatedFieldName (field);
if (!rf.equals (""))
{
// bidirectional relationship...
MethodDoc relField = findCMRField (related, rf);
name2 += ("." + getFieldName (relField));
// check from the related fields view
rt = relField.returnType ().qualifiedTypeName ();
if (rt.equals ("java.util.Collection") ||
rt.equals ("java.util.Set"))
{
// to many
relation.role1.multiplicity = "many";
relation.role2.cmrFieldType = rt;
} else
{
// to one
relation.role1.multiplicity = "one";
relation.role2.cmrFieldType = "";
}
relation.role2.cmrFieldName = getFieldName (relField);
} else
{
// unidirectional relationship...
// check from the related fields view - must be "one"
relation.role1.multiplicity = "one";
relation.role2.cmrFieldType = "";
relation.role2.cmrFieldName = "";
}
if (relations.get (name2+"-"+name1) == null)
{
// relation does not jet exist...
relation.name = name1 + "-" + name2;
// create role names...
if (relation.role1.multiplicity.equals ("one"))
{
if (relation.role2.multiplicity.equals ("one"))
{
// one2one
relation.role1.name = current.name () + " has one " +
related.name ();
relation.role2.name = related.name () + " belongs to
one " + current.name ();
} else
{
// one2many
relation.role1.name = current.name () + " has many " +
related.name () + "s";
relation.role2.name = related.name () + " belongs to
one " + current.name ();
}
} else
{
if (relation.role2.multiplicity.equals ("one"))
{
// many2one
relation.role1.name = current.name () + " belongs to
one " + related.name ();
relation.role2.name = related.name () + " has many " +
current.name () + "s";
} else
{
// many2many
relation.role1.name = current.name () + " has many " +
related.name () + "s";
relation.role2.name = related.name () + " has many " +
current.name () + "s";
}
}
// create source entries
relation.role1.sourceType = isCMPEntity
(current)?"ejb-name":(isDependentObject (current)?"dependent-name":"???");
relation.role1.sourceName = getEJBName (current);
relation.role2.sourceType = isCMPEntity
(related)?"ejb-name":(isDependentObject (related)?"dependent-name":"???");
relation.role2.sourceName = getEJBName (related);
// ...and create it.
relations.put (relation.name, relation);
log.println ("relationship \""+relation.name+"\" added.");
} else
log.println ("relationship \""+name2+"-"+name1+"\" already
exists.");
} catch (Exception _e) {
log.println ("ERROR: "+_e.getMessage ()+" skip");
}
}
}
/** Finds the named class in the repository.
* @param _className the name of the class to look for
* @return a ClassDoc object for the class
* @throws Exception if this class wasnt found */
protected ClassDoc getClassForName (String _className) throws Exception
{
ClassDoc result = root.classNamed (_className);
if (result == null)
throw new Exception ("Class \""+_className+"\" not in repository!");
return result;
}
/** Returns the fields name from a given getter or setter method.
* @param _m a getter or setter method for a field
* @return the name of the field. (methodname minus the first 3 letters,
* fist letter moved to lowercase) */
protected String getFieldName (MethodDoc _m)
{
String uppercase = _m.name ().substring (3);
// return uppercase;
char[] chars = uppercase.toCharArray ();
chars[0] = Character.toLowerCase (chars[0]);
return new String (chars);
}
/** Returns all classes with the <code><b>stateless-session</b></code> tag
* @return Iterator over all stateless session classes */
protected Iterator getStatelessSessions ()
{
ClassDoc[] c = root.classes ();
Vector result = new Vector ();
for (int i = 0, l = c.length; i < l; ++i)
{
if (c[i].tags ("stateless-session").length > 0)
result.add (c[i]);
}
return result.iterator ();
}
/** Returns all classes with the <code><b>stateful-session</b></code> tag
* @return Iterator over all stateful session classes */
protected Iterator getStatefulSessions ()
{
ClassDoc[] c = root.classes ();
Vector result = new Vector ();
for (int i = 0, l = c.length; i < l; ++i)
{
if (c[i].tags ("stateful-session").length > 0)
result.add (c[i]);
}
return result.iterator ();
}
/** Returns all classes with the <code><b>entity-cmp</b></code> tag
* @return Iterator over all CMP entities classes */
protected Iterator getCMPEntities ()
{
ClassDoc[] c = root.classes ();
Vector result = new Vector ();
for (int i = 0, l = c.length; i < l; ++i)
{
if (c[i].tags ("entity-cmp").length > 0)
result.add (c[i]);
}
return result.iterator ();
}
/** Returns all classes with the <code><b>dependent-object</b></code> tag
* @return Iterator over all dependent object classes */
protected Iterator getDependentObjects ()
{
ClassDoc[] c = root.classes ();
Vector result = new Vector ();
for (int i = 0, l = c.length; i < l; ++i)
{
if (c[i].tags ("dependent-object").length > 0)
result.add (c[i]);
}
return result.iterator ();
}
/** Returns all getter methods with the <code><b>cmp-field</b></code> tag
* @param _clazz the class to inspect
* @return Iterator over all CMP fields of the give class */
protected Iterator getCMPFields (ClassDoc _clazz)
{
MethodDoc[] m = _clazz.methods ();
Vector result = new Vector ();
for (int i = 0, l = m.length; i < l; ++i)
{
if (m[i].tags ("cmp-field").length > 0 &&
m[i].name ().startsWith ("get"))
result.add (m[i]);
}
return result.iterator ();
}
/** Returns all getter methods with the <code><b>cmr-field</b></code> tag
* @param _clazz the class to inspect
* @return Iterator over all CMR fields of the give class */
protected Iterator getCMRFields (ClassDoc _clazz)
{
MethodDoc[] m = _clazz.methods ();
Vector result = new Vector ();
for (int i = 0, l = m.length; i < l; ++i)
{
if (m[i].tags ("cmr-field").length > 0 &&
m[i].name ().startsWith ("get"))
result.add (m[i]);
}
return result.iterator ();
}
/** Returns all create methods
* @param _clazz the class to inspect
* @return Iterator over all create methods of the give class */
protected Iterator getCreateMethods (ClassDoc _clazz)
{
MethodDoc[] m = _clazz.methods ();
Vector result = new Vector ();
for (int i = 0, l = m.length; i < l; ++i)
{
if (m[i].name ().startsWith ("ejbCreate"))
result.add (m[i]);
}
return result.iterator ();
}
/** Returns all home methods
* @param _clazz the class to inspect
* @return Iterator over all home methods of the give class */
protected Iterator getHomeMethods (ClassDoc _clazz)
{
MethodDoc[] m = _clazz.methods ();
Vector result = new Vector ();
for (int i = 0, l = m.length; i < l; ++i)
{
if (m[i].name ().startsWith ("ejbHome"))
result.add (m[i]);
}
return result.iterator ();
}
/** Returns whether the value of the <code><b>ejb-name</b></code> or the
* <code><b>dependent-name</b></code> tag of the given class. Or an empty String
* if none is set.
* @param _clazz the class to inspect
* @return ejb-name or dependent-name or empty string */
protected String getEJBName (ClassDoc _c)
{
if (_c.tags ("ejb-name").length > 0)
return _c.tags ("ejb-name")[0].text ();
if (_c.tags ("dependent-name").length > 0)
return _c.tags ("dependent-name")[0].text ();
return "";
}
/** Returns true if the<code><b>entity-cmp</b></code> tag is set
* @param _clazz the class to inspect
* @return true, if entity-cmp tag is set */
protected boolean isCMPEntity (ClassDoc _c)
{
return (_c.tags ("entity-cmp").length > 0);
}
/** Returns true if the <code><b>dependent-object</b></code> tag is set
* @param _clazz the class to inspect
* @return true, if dependent-object tag is set */
protected boolean isDependentObject (ClassDoc _c)
{
return (_c.tags ("dependent-object").length > 0);
}
/** Returns the name of the PrimaryKey class for the given class.
* @param class to inspect
* @return the PrimaryKeys class name or empty string if none */
protected String getPKName (ClassDoc _clazz)
{
String result = "";
Iterator i = getCreateMethods (_clazz);
if (i.hasNext ())
result = ((MethodDoc)i.next ()).returnType ().qualifiedTypeName ();
return result;
}
/** Returns the getDATA method if defined else null
* @param class to inspect
* @return the MethodDoc object for the getDATA () method */
protected MethodDoc getDataMethod (ClassDoc _clazz)
{
MethodDoc[] m = _clazz.methods ();
for (int i = 0, l = m.length; i < l; ++i)
{
if (m[i].tags ("data-method").length > 0 &&
m[i].name ().startsWith ("get"))
return m[i];
}
return null;
}
/** Assumed that the given method is a get_cmr-field_ method, this method tries to
* figure out the related class of this field. <br>
* If the returntype of the method is a collection, the
<code><b>cmr-related-name</b></code>
* tag is looked for. The found class must have an <code><b>ejb-name</b></code>
tag
* to be given back as result.
* @param _m the getCMRFIELD () method
* @return the ClassDoc of the related ejb object
* @throws Exception if no fitting class was found */
protected ClassDoc getRelatedClass (MethodDoc _m) throws Exception
{
String returntype = _m.returnType ().qualifiedTypeName ();
if (returntype.equals ("java.util.Collection") ||
returntype.equals ("java.util.Set"))
{
// Oops! then we must assume we have a cmr-related-name tag...
if (_m.tags ("cmr-related-name").length > 0)
{
returntype = _m.tags ("cmr-related-name")[0].text ();
}
else
returntype = "";
}
ClassDoc result = null;
try { result = root.classNamed (returntype);} catch (Exception _e) {}
if (result != null && !getEJBName (result).equals (""))
return result;
// maby it is an RemoteInterface that not yet exists?
try { result = root.classNamed (returntype+"Bean");} catch (Exception _e) {}
if (result != null && !getEJBName (result).equals (""))
return result;
throw new Exception ("related class \""+returntype+"\" not specified or
found!");
}
/** Returns the fieldname of the related field if this is a bidirectional
relationship.
* If no <code><b>cmr-related-field-name</b></code> tag is set, it is assumed
that this
* is an unidirectional relationship.
* @param the cmr-field mehtod to inspect
* @return the value of the cmr-related-field-name tag or empty string*/
protected String getRelatedFieldName (MethodDoc _m)
{
String result = "";
if (_m.tags ("cmr-related-field-name").length > 0)
{
result = _m.tags ("cmr-related-field-name")[0].text ();
}
return result;
}
/** Tries to find the getCMR-FIELD () method of the given class with the given
* field name.
* @param _clazz the class to inspect
* @param _fieldName the name of the field to look for
* @return the MethodDoc object of the getCMR-FIELD () method
* @throws Exception if no such field were found */
protected MethodDoc findCMRField (ClassDoc _clazz, String _fieldName) throws
Exception
{
Iterator i = getCMRFields (_clazz);
while (i.hasNext ())
{
MethodDoc m = (MethodDoc) i.next ();
String fieldName = getFieldName (m);
if (_fieldName.equalsIgnoreCase (fieldName))
return m;
}
throw new Exception ("No field with name \""+_fieldName+"\" found!");
}
// private -------------------------------------------------------
// InnerClass ----------------------------------------------------
class Relation
{
Relation ()
{
role1 = new Role ();
role2 = new Role ();
}
public String name; //
public Role role1; //
public Role role2; //
}
class Role
{
public String name;
public String multiplicity; //
public String sourceType;
public String sourceName;
public String cmrFieldName; //
public String cmrFieldType; //
}
}
---8<-- The Generated XML File ----------------------------------------
<ejb-jar>
<description>Test</description>
<display-name>Test</display-name>
<enterprise-beans>
<entity>
<description></description>
<ejb-name>Order</ejb-name>
<home>cmp20.OrderHome</home>
<remote>cmp20.Order</remote>
<ejb-class>cmp20.OrderCMP</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>cmp20.OrderPK</prim-key-class>
<reentrant>True</reentrant>
<cmp-field>
<description></description>
<field-name>Id</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>Date</field-name>
</cmp-field>
<ejb-ref>
<ejb-ref-name>ejb/Customer</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>cmp20.CustomerHome</home>
<remote>cmp20.Customer</remote>
<ejb-link>Customer</ejb-link>
</ejb-ref>
<ejb-ref>
<ejb-ref-name>ejb/Product</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>cmp20.ProductHome</home>
<remote>cmp20.Product</remote>
<ejb-link>Product</ejb-link>
</ejb-ref>
</entity>
<entity>
<description></description>
<ejb-name>Product</ejb-name>
<home>cmp20.ProductHome</home>
<remote>cmp20.Product</remote>
<ejb-class>cmp20.ProductCMP</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>cmp20.ProductPK</prim-key-class>
<reentrant>True</reentrant>
<cmp-field>
<description></description>
<field-name>SerialNumber</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>Name</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>Description</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>Price</field-name>
</cmp-field>
</entity>
<entity>
<description></description>
<ejb-name>Customer</ejb-name>
<home>cmp20.CustomerHome</home>
<remote>cmp20.Customer</remote>
<ejb-class>cmp20.CustomerCMP</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>cmp20.CustomerPK</prim-key-class>
<reentrant>True</reentrant>
<cmp-field>
<description></description>
<field-name>Id</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>Name</field-name>
</cmp-field>
<cmp-field>
<description></description>
<field-name>CredidCard</field-name>
</cmp-field>
</entity>
</enterprise-beans>
<assembly-descriptor>
</assembly-descriptor>
<relationships>
<ejb-relation>
<ejb-relation-name>OrderBean.orderLines-OrderLine.order</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>OrderBean has many
OrderLines</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>Order</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>orderLines<cmr-field-name>
<cmr-field-type>java.util.Collection<cmr-field-type>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>OrderLine belongs to one
OrderBean</ejb-relationship-role-name>
<multiplicity>many</multiplicity>
<role-source>
<dependent-name>OrderLine</dependent-name>
</role-source>
<cmr-field>
<cmr-field-name>order<cmr-field-name>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
<ejb-relation>
<ejb-relation-name>OrderBean.customer-CustomerBean.orders</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>OrderBean belongs to one
CustomerBean</ejb-relationship-role-name>
<multiplicity>many</multiplicity>
<role-source>
<ejb-name>Order</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>customer<cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>CustomerBean has many
OrderBeans</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>Customer</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>orders<cmr-field-name>
<cmr-field-type>java.util.Collection<cmr-field-type>
</cmr-field>
</ejb-relationship-role>
</ejb-relation>
<ejb-relation>
<ejb-relation-name>CustomerBean.deliveryAddress-Address</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>CustomerBean has one
Address</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>Customer</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>deliveryAddress<cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Address belongs to one
CustomerBean</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<dependent-name>Address</dependent-name>
</role-source>
</ejb-relationship-role>
</ejb-relation>
<ejb-relation>
<ejb-relation-name>OrderLine.product-ProductBean</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>OrderLine has one
ProductBean</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<dependent-name>OrderLine</dependent-name>
</role-source>
<cmr-field>
<cmr-field-name>product<cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>ProductBean belongs to one
OrderLine</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>Product</ejb-name>
</role-source>
</ejb-relationship-role>
</ejb-relation>
<ejb-relation>
<ejb-relation-name>CustomerBean.billingAddress-Address</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>CustomerBean has one
Address</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<ejb-name>Customer</ejb-name>
</role-source>
<cmr-field>
<cmr-field-name>billingAddress<cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<ejb-relationship-role-name>Address belongs to one
CustomerBean</ejb-relationship-role-name>
<multiplicity>one</multiplicity>
<role-source>
<dependent-name>Address</dependent-name>
</role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
</ejb-jar>
--
--------------------------------------------------------------
To subscribe: [EMAIL PROTECTED]
To unsubscribe: [EMAIL PROTECTED]
Problems?: [EMAIL PROTECTED]