User: dirk
Date: 01/02/12 01:23:00
Modified: src/main/org/jboss/ejb/plugins/jaws/metadata
CMPFieldMetaData.java JawsEntityMetaData.java
Log:
JAWS patch for using nested fields.
Revision Changes Path
1.3 +219 -6
jboss/src/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java
Index: CMPFieldMetaData.java
===================================================================
RCS file:
/products/cvs/ejboss/jboss/src/main/org/jboss/ejb/plugins/jaws/metadata/CMPFieldMetaData.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- CMPFieldMetaData.java 2000/12/07 15:44:40 1.2
+++ CMPFieldMetaData.java 2001/02/12 09:23:00 1.3
@@ -19,7 +19,10 @@
import org.jboss.metadata.MetaData;
import org.jboss.metadata.EjbRefMetaData;
+import java.util.*;
+import org.jboss.logging.Log;
+import org.jboss.logging.Logger;
/**
* This class holds all the information jaws needs to know about a CMP field
@@ -27,7 +30,8 @@
*
* @see <related>
* @author <a href="[EMAIL PROTECTED]">Sebastien Alborini</a>
- * @version $Revision: 1.2 $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Dirk Zimmermann</a>
+ * @version $Revision: 1.3 $
*/
public class CMPFieldMetaData extends MetaData implements XmlLoadable {
// Constants -----------------------------------------------------
@@ -57,6 +61,22 @@
private boolean isAPrimaryKeyField;
+ /**
+ * We need this for nested field retrieval.
+ */
+ private String ejbClassName;
+
+ /**
+ * We need this for nested fields. We could compute it from ejbClassName on
the fly,
+ * but it's faster to set it once and cache it.
+ */
+ private Class ejbClass;
+
+ /**
+ * Is true for fields like "data.categoryPK".
+ */
+ private boolean isNested;
+
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
@@ -64,18 +84,23 @@
this.name = name;
this.jawsEntity = jawsEntity;
- String ejbClassName = jawsEntity.getEntity().getEjbClass();
+ // save the class name for nested fields
+ ejbClassName = jawsEntity.getEntity().getEjbClass();
+ ejbClassName = jawsEntity.getEntity().getEjbClass();
+
try {
- Class ejbClass =
jawsEntity.getJawsApplication().getClassLoader().loadClass(ejbClassName);
+ // save the class for nested fields
+ 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);
+ // we can't throw an Exception here, because we could have a
nested field
+ checkField();
}
// default, may be overridden by importXml
- columnName = name;
+ columnName = getLastComponent(name);
// cannot set defaults for jdbctype/sqltype, type mappings are not
loaded yet.
}
@@ -111,7 +136,185 @@
public JawsEntityMetaData getJawsEntity() { return jawsEntity; }
-
+ /**
+ * 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;
+ }
+
+ /**
+ * Returns the first component of a composite fieldName. E.g., for
"data.categoryPK" it
+ * will return "data".
+ */
+ public static String getFirstComponent(String name) {
+ String fieldName;
+ StringTokenizer st = new StringTokenizer(name, ".");
+ if (st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ }
+ else {
+ fieldName = null;
+ }
+ return fieldName;
+ }
+
+ /**
+ * Detects the actual field of a nested field and sets field accordingly.
+ * If field doesn't exist, throws a DeploymentException.
+ */
+ private void checkField() throws DeploymentException {
+ try {
+ field = verifyNestedField();
+ }
+ catch(DeploymentException e) {
+ // try it again, but debug Class before :))
+ debugClass(ejbClass);
+ field = verifyNestedField();
+ Log.getLog().warning("!!! using buggy hotspot, try to upgrade
... !!!");
+ }
+ Log.getLog().warning("mapping " + name + " to " +
getLastComponent(name));
+ }
+
+ /**
+ * Traverses and verifies a nested field, so that every field given in jaws.xml
+ * exists in the Bean.
+ */
+ private Field verifyNestedField() throws DeploymentException {
+ String fieldName = null;
+ Field tmpField = null;
+ Class tmpClass = ejbClass;
+ StringTokenizer st = new StringTokenizer(name, ".");
+
+ if (st.countTokens() > 1) {
+ isNested = true;
+ }
+
+ while(st.hasMoreTokens()) {
+ fieldName = st.nextToken();
+ try {
+ //debugClass(tmpClass);
+ tmpField = tmpClass.getField(fieldName);
+ tmpClass = tmpField.getType();
+ }
+ catch (NoSuchFieldException e) {
+ throw new DeploymentException("cmp-field " + name + "
is not a field in ejb class " + ejbClassName);
+ }
+ }
+ return tmpField;
+ }
+
+ /**
+ * We don't rely on the field alone 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();
+ }
+
+ /**
+ * Is used mainly for nested fields. Sets the value of a nested 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);
+ }
+ }
+
+ /**
+ * Returns the value of this field.
+ */
+ public Object getValue(Object instance) {
+ String fieldName;
+ Object currentObject = instance;
+ Field currentField;
+ //Object currentValue = null;
+
+ try {
+ if (!isNested()) {
+ 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) {
+ // We have already checked the presence of this field in the
constructor,
+ // so there is no need to throw an exception here.
+ Log.getLog().warning("!!! CMPFieldMetaData.getValue() ERROR
!!! " + e);
+ }
+ catch (NoSuchFieldException e) {
+ // We have already checked the presence of this field in the
constructor,
+ // so there is no need to throw an exception here.
+ Log.getLog().warning("!!! CMPFieldMetaData.getValue() ERROR
!!! " + e);
+ }
+ return null;
+ }
+
+ public boolean isNested() {
+ return isNested;
+ }
+
// XmlLoadable implementation ------------------------------------
public void importXml(Element element) throws DeploymentException {
@@ -141,6 +344,16 @@
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
+
+ /**
+ * Workaround for certain Hotspot problems. Just traverse all the fields
+ * in the Class, so Hotspot won't optimize to bad ...
+ */
+ private void debugClass(Class debugClass) {
+ Field[] fields = debugClass.getFields();
+ for (int i = 0; i < fields.length; ++i) {
+ }
+ }
// Inner classes -------------------------------------------------
}
1.6 +53 -5
jboss/src/main/org/jboss/ejb/plugins/jaws/metadata/JawsEntityMetaData.java
Index: JawsEntityMetaData.java
===================================================================
RCS file:
/products/cvs/ejboss/jboss/src/main/org/jboss/ejb/plugins/jaws/metadata/JawsEntityMetaData.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- JawsEntityMetaData.java 2001/01/24 20:36:43 1.5
+++ JawsEntityMetaData.java 2001/02/12 09:23:00 1.6
@@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
+import java.util.HashMap;
import java.lang.reflect.Field;
@@ -24,13 +25,16 @@
import org.jboss.metadata.MetaData;
import org.jboss.metadata.XmlLoadable;
+// TODO
+import org.jboss.logging.Logger;
/**
* <description>
*
* @see <related>
* @author <a href="[EMAIL PROTECTED]">Sebastien Alborini</a>
- * @version $Revision: 1.5 $
+ * @author <a href="mailto:[EMAIL PROTECTED]">Dirk Zimmermann</a>
+ * @version $Revision: 1.6 $
*/
public class JawsEntityMetaData extends MetaData implements XmlLoadable {
// Constants -----------------------------------------------------
@@ -78,7 +82,18 @@
// finders for this bean
private ArrayList finders = new ArrayList();
-
+ /**
+ * Here we store the basename of all detailed fields in jaws.xml
+ * (e.g., "data" for "data.categoryPK").
+ */
+ private HashMap detailedFieldDescriptions = new HashMap();
+
+ /**
+ * This is the Boolean we store as value in detailedFieldDescriptions.
+ */
+ private static final Boolean detailedBoolean = new Boolean(true);
+
+
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
@@ -229,9 +244,20 @@
String fieldName = getElementContent(getUniqueChild(cmpField,
"field-name"));
CMPFieldMetaData cmpFieldMetaData =
getCMPFieldByName(fieldName);
- if (cmpFieldMetaData == null)
- throw new DeploymentException("cmp-field
'"+fieldName+"' found in jaws.xml but not in ejb-jar.xml");
-
+ if (cmpFieldMetaData == null) {
+ // Before we throw an exception, we have to check for
nested cmp-fields.
+ // We just add a new CMPFieldMetaData.
+ if (isDetailedFieldDescription(fieldName)) {
+ // We obviously have a cmp-field like
"data.categoryPK" in jaws.xml
+ // and a cmp-field "data" in ejb-jar.xml.
+ // In this case, we assume the
"data.categoryPK" as a detailed description for "data".
+ cmpFieldMetaData = new
CMPFieldMetaData(fieldName, this);
+ cmpFields.put(fieldName, cmpFieldMetaData);
+ }
+ else {
+ throw new DeploymentException("cmp-field
'"+fieldName+"' found in jaws.xml but not in ejb-jar.xml");
+ }
+ }
cmpFieldMetaData.importXml(cmpField);
}
@@ -246,6 +272,28 @@
finders.add(finderMetaData);
}
+ }
+
+ /**
+ * @return true For a fieldname declared in jaws.xml like "data.categoryPK" if
+ * there was a fieldname declared in ejb-jar.xml like "data".
+ */
+ private boolean isDetailedFieldDescription(String fieldName) {
+ String fieldBaseName = CMPFieldMetaData.getFirstComponent(fieldName);
+
+ if (detailedFieldDescriptions.containsKey(fieldBaseName)) {
+ return true;
+ }
+
+ CMPFieldMetaData cmpFieldMetaData = getCMPFieldByName(fieldBaseName);
+ if (cmpFieldMetaData == null) {
+ return false;
+ }
+ else {
+ detailedFieldDescriptions.put(fieldBaseName, detailedBoolean);
+ cmpFields.remove(fieldBaseName);
+ return true;
+ }
}