Hello,
this is a patch for jBoss (cvs-snapshot from 2001-01-30) that enables
nested fields like this:
[...]
<cmp-field>
<field-name>data.categoryPK</field-name>
</cmp-field>
[...]
With this patch, you no longer have to follow the advice on
http://www.jboss.org/newsite/manual/developing.html#entity
(the EMailAddress example).
Unfortunately, there is no fixed jBoss version I can patch against. If
you are interested, I can send you the original snapshot and the patch
by mail.
Nevertheless, I have included the output from patch so you can get the
idea.
--
/// Dirk.
diff -Naur orig/main/org/jboss/ejb/plugins/CMPPersistenceManager.java
patched/main/org/jboss/ejb/plugins/CMPPersistenceManager.java
--- orig/main/org/jboss/ejb/plugins/CMPPersistenceManager.java Fri Jan 26 21:31:24
2001
+++ patched/main/org/jboss/ejb/plugins/CMPPersistenceManager.java Fri Feb 09
+10:48:39 2001
@@ -33,6 +33,8 @@
import org.jboss.ejb.EntityPersistenceStore;
import org.jboss.metadata.EntityMetaData;
+import org.jboss.logging.Logger;
+
/**
* The CMP Persistence Manager implements the semantics of the CMP
* EJB 1.1 call back specification.
@@ -146,7 +148,7 @@
cmpFieldType = cmpField.getType();
// find the type of the field and reset it
// to the default value
- if (cmpFieldType.equals(boolean.class)) {
+ if ( cmpFieldType.equals(boolean.class)) {
cmpField.setBoolean(instance,false);
} else if (cmpFieldType.equals(byte.class)) {
cmpField.setByte(instance,(byte)0);
diff -Naur orig/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCCommand.java
patched/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCCommand.java
--- orig/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCCommand.java Thu Dec 07 16:44:36
2000
+++ patched/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCCommand.java Thu Feb 08
+19:07:30 2001
@@ -103,7 +103,7 @@
/**
* Gives compile-time control of tracing.
*/
- public static boolean debug = false;
+ public static boolean debug = true;
// Constructors --------------------------------------------------
@@ -355,6 +355,25 @@
}
+ /**
+ * DZ
+ * Wrapper around getResultObject(ResultSet rs, int idx, Class destination).
+ */
+ protected Object getResultObject(ResultSet rs, int idx, CMPFieldMetaData
+cmpField)
+ throws SQLException {
+ if (!cmpField.isComposite()) {
+ // do it as before
+ return getResultObject(rs, idx, cmpField.getField().getType());
+ }
+
+ // deal with composite object
+ log.warning("Using Inprise feature.");
+
+ // Assuming no one will ever use BLOPS in composite objects.
+ // TODO Should be tested for BLOPability
+ return rs.getObject(idx);
+ }
+
/**
* Used for all retrieval of results from <code>ResultSet</code>s.
* Implements tracing, and allows some tweaking of returned types.
@@ -578,8 +597,10 @@
protected Object getCMPFieldValue(Object instance, CMPFieldMetaData fieldMetaData)
throws IllegalAccessException
{
- Field field = fieldMetaData.getField();
- return field.get(instance);
+ // DZ
+ //Field field = fieldMetaData.getField();
+ //return field.get(instance);
+ return fieldMetaData.getValue(instance);
}
protected void setCMPFieldValue(Object instance,
@@ -587,8 +608,16 @@
Object value)
throws IllegalAccessException
{
- Field field = fieldMetaData.getField();
- field.set(instance, value);
+ // DZ
+ if (fieldMetaData.isComposite()) {
+ // we have a composite field
+ fieldMetaData.set(instance, value);
+ }
+ else {
+ // the usual way
+ Field field = fieldMetaData.getField();
+ field.set(instance, value);
+ }
}
protected Object getPkFieldValue(Object pk, PkFieldMetaData pkFieldMetaData)
diff -Naur orig/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCLoadEntityCommand.java
patched/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCLoadEntityCommand.java
--- orig/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCLoadEntityCommand.java Thu
Dec 07 16:44:38 2000
+++ patched/main/org/jboss/ejb/plugins/jaws/jdbc/JDBCLoadEntityCommand.java Thu
+Feb 08 16:28:23 2001
@@ -111,7 +111,9 @@
setCMPFieldValue(ctx.getInstance(),
cmpField,
- getResultObject(rs, idx++, cmpField.getField().getType()));
+
+ // DZ
+ //getResultObject(rs, idx++,
+cmpField.getField().getType()));
+ getResultObject(rs, idx++, cmpField));
}
// Store state to be able to do tuned updates
diff -Naur orig/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java
patched/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java
--- orig/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java Thu Dec 07
16:44:40 2000
+++ patched/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java Thu
+Feb 08 19:25:41 2001
@@ -11,6 +11,9 @@
import java.util.ArrayList;
import java.util.Iterator;
+// DZ
+import java.util.*;
+
import org.w3c.dom.Element;
import org.jboss.ejb.DeploymentException;
@@ -19,6 +22,9 @@
import org.jboss.metadata.MetaData;
import org.jboss.metadata.EjbRefMetaData;
+// DZ
+import org.jboss.logging.Log;
+import org.jboss.logging.Logger;
/**
@@ -55,6 +61,25 @@
private String columnName;
private boolean isAPrimaryKeyField;
+
+ /**
+ * DZ
+ * We need this for the Inprise Hack.
+ */
+ private String ejbClassName;
+
+
+ /**
+ * DZ
+ * We need this for the Inprise Hack.
+ */
+ private Class ejbClass;
+
+ /**
+ * DZ
+ * Is set for fields like "data.categoryPK".
+ */
+ private boolean isComposite;
// Static --------------------------------------------------------
@@ -64,18 +89,25 @@
this.name = name;
this.jawsEntity = jawsEntity;
- String ejbClassName = jawsEntity.getEntity().getEjbClass();
+ // DZ
+ ejbClassName = jawsEntity.getEntity().getEjbClass();
+
try {
- Class ejbClass =
jawsEntity.getJawsApplication().getClassLoader().loadClass(ejbClassName);
- field = ejbClass.getField(name);
+ ejbClass =
+jawsEntity.getJawsApplication().getClassLoader().loadClass(ejbClassName);
+ field = ejbClass.getField(name);
} catch (ClassNotFoundException e) {
throw new DeploymentException("ejb class not found: " +
ejbClassName);
- } catch (NoSuchFieldException e) {
- throw new DeploymentException("cmp-field " + name + " is not a
field in ejb class " + ejbClassName);
+ }
+ catch (NoSuchFieldException e) {
+ //throw new DeploymentException("cmp-field " + name +
+" is not a field in ejb class " + ejbClassName);
+ Log.getLog().warning("Warning: You rely on an Inprise
+Application Server feature with " +
+
+ "field " + name + " in " + ejbClassName);
+ checkField();
}
// default, may be overridden by importXml
- columnName = name;
+ // DZ
+ columnName = getLastComponent(name);
// cannot set defaults for jdbctype/sqltype, type mappings are not
loaded yet.
}
@@ -95,6 +127,147 @@
return jdbcType;
}
+ /**
+ * DZ
+ * Returns the last component of a composite fieldName. E.g., for
+"data.categoryPK" it
+ * will return "categoryPK".
+ */
+ public static String getLastComponent(String name) {
+ String fieldName = name;
+ StringTokenizer st = new StringTokenizer(name, ".");
+ while(st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ }
+ return fieldName;
+ }
+
+ /**
+ * DZ
+ * Detects the actual field of a composite field and sets field accordingly.
+ */
+ private void checkField() {
+ Field tmpField = null;
+ Class tmpClass = ejbClass;
+ String fieldName = null;
+ StringTokenizer st = new StringTokenizer(name, ".");
+
+ if (st.countTokens() > 1) {
+ isComposite = true;
+ }
+
+ while(st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ try {
+ tmpField = tmpClass.getField(fieldName);
+ tmpClass = tmpField.getType();
+ }
+ catch (NoSuchFieldException e) {
+ Log.getLog().warning("!!! Deployment Failure !!!");
+ }
+ }
+ field = tmpField;
+ }
+
+ /**
+ * DZ
+ * We don't rely on the field for getting the type since we support nested
+field
+ * like 'data.categoryPK'.
+ */
+ public Class getFieldType() {
+ if (field != null) {
+ // The default case as it always was :)
+ return field.getType();
+ }
+
+ // We obviously have a nested field (or an erroneous one)
+ Field tmpField = null;
+ Class tmpClass = ejbClass;
+ String fieldName = null;
+ StringTokenizer st = new StringTokenizer(name, ".");
+ while(st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ try {
+ tmpField = tmpClass.getField(fieldName);
+ tmpClass = tmpField.getType();
+ }
+ catch (NoSuchFieldException e) {
+ Log.getLog().warning("!!! Deployment Failure !!!");
+ }
+ }
+ return tmpField.getType();
+ }
+
+ /**
+ * DZ
+ * Is used mainly for composite fields. Sets the value of a composite field.
+ */
+ public void set(Object instance, Object value) {
+ Field tmpField = null;
+ String fieldName = null;
+ Object currentObject = instance;
+ Object oldObject;
+ StringTokenizer st = new StringTokenizer(name, ".");
+
+ try {
+ for (int i = 0; i < st.countTokens() - 1; ++i) {
+ st.hasMoreTokens();
+ fieldName = st.nextToken();
+ tmpField =
+currentObject.getClass().getField(fieldName);
+ oldObject = currentObject;
+ currentObject = tmpField.get(currentObject);
+ // On our path, we have to instantiate every
+intermediate object
+ if (currentObject == null) {
+ currentObject =
+tmpField.getType().newInstance();
+ tmpField.set(oldObject, currentObject);
+ }
+ }
+ Field dataField =
+currentObject.getClass().getField(getLastComponent(name));
+ dataField.set(currentObject, value);
+ }
+ catch (NoSuchFieldException e) {
+ Log.getLog().warning("!!! Deployment Failure !!!");
+ }
+ catch (IllegalAccessException e) {
+ Log.getLog().warning("!!! Deployment Failure !!!");
+ }
+ catch (InstantiationException e) {
+ Log.getLog().warning("could not instantiate " + tmpField);
+ }
+ }
+
+ /**
+ * DZ
+ * Returns the value of this field.
+ */
+ public Object getValue(Object instance) {
+ String fieldName;
+ Object currentObject = instance;
+ Field currentField;
+ //Object currentValue = null;
+
+ try {
+ if (!isComposite()) {
+ return getField().get(instance);
+ }
+ else {
+ StringTokenizer st = new StringTokenizer(name, ".");
+ while(st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ currentField =
+currentObject.getClass().getField(fieldName);
+ currentObject =
+currentField.get(currentObject);
+ }
+ return currentObject;
+ }
+ }
+ catch (IllegalAccessException e) {
+ Log.getLog().warning("!!! CMPFieldMetaData.getValue() ERROR
+!!! " + e);
+ }
+ catch (NoSuchFieldException e) {
+ Log.getLog().warning("!!! CMPFieldMetaData.getValue() ERROR
+!!! " + e);
+ }
+ return null;
+ }
+
public String getSQLType() {
if (sqlType == null) {
// set the default
@@ -112,7 +285,15 @@
public JawsEntityMetaData getJawsEntity() { return jawsEntity; }
- // XmlLoadable implementation ------------------------------------
+ // DZ
+ public boolean isComposite() {
+ return isComposite;
+ }
+ public void isComposite(boolean v) {
+ this.isComposite = v;
+ }
+
+ // XmlLoadable implementation ------------------------------------
public void importXml(Element element) throws DeploymentException {
// column name