0001: /*
0002: * ====================================================================
0003: * JAFFA - Java Application Framework For All
0004: *
0005: * Copyright (C) 2002 JAFFA Development Group
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this library; if not, write to the Free Software
0019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0020: *
0021: * Redistribution and use of this software and associated documentation ("Software"),
0022: * with or without modification, are permitted provided that the following conditions are met:
0023: * 1. Redistributions of source code must retain copyright statements and notices.
0024: * Redistributions must also contain a copy of this document.
0025: * 2. Redistributions in binary form must reproduce the above copyright notice,
0026: * this list of conditions and the following disclaimer in the documentation
0027: * and/or other materials provided with the distribution.
0028: * 3. The name "JAFFA" must not be used to endorse or promote products derived from
0029: * this Software without prior written permission. For written permission,
0030: * please contact mail to: jaffagroup@yahoo.com.
0031: * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
0032: * appear in their names without prior written permission.
0033: * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
0034: *
0035: * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
0036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0046: * SUCH DAMAGE.
0047: * ====================================================================
0048: */
0049:
0050: /*
0051: * BeanMolder.java
0052: *
0053: * Created on February 12, 2004, 4:00 PM
0054: */
0055:
0056: package org.jaffa.beans.moulding.mapping;
0057:
0058: import java.beans.BeanInfo;
0059: import java.beans.IntrospectionException;
0060: import java.beans.Introspector;
0061: import java.beans.PropertyDescriptor;
0062: import java.lang.reflect.Array;
0063: import java.lang.reflect.Constructor;
0064: import java.lang.reflect.InvocationTargetException;
0065: import java.lang.reflect.Method;
0066: import java.util.ArrayList;
0067: import java.util.HashMap;
0068: import java.util.Iterator;
0069: import java.util.LinkedHashMap;
0070: import java.util.List;
0071: import java.util.Map;
0072: import java.util.Set;
0073: import org.apache.log4j.Logger;
0074: import org.jaffa.beans.moulding.data.domain.DomainDAO;
0075: import org.jaffa.beans.moulding.mapping.GraphMapping;
0076: import org.jaffa.beans.moulding.mapping.MappingFilter;
0077: import org.jaffa.exceptions.ApplicationException;
0078: import org.jaffa.exceptions.ApplicationExceptions;
0079: import org.jaffa.exceptions.DomainObjectNotFoundException;
0080: import org.jaffa.exceptions.FrameworkException;
0081: import org.jaffa.exceptions.MultipleDomainObjectsFoundException;
0082: import org.jaffa.persistence.Criteria;
0083: import org.jaffa.persistence.IPersistent;
0084: import org.jaffa.persistence.Persistent;
0085: import org.jaffa.persistence.UOW;
0086: import org.jaffa.persistence.util.PersistentHelper;
0087: import org.jaffa.util.StringHelper;
0088:
0089: /** Bean Moudler is used to mapp data between two Java Beans via a mapping file.
0090: * It has been specifcially coded to map between benas that extend/implement
0091: * DomainDAO and IPersistent for marshalling data to and from the database.
0092: *
0093: * @author PaulE
0094: * @version 1.0
0095: */
0096: public class BeanMoulder {
0097:
0098: private static Logger log = Logger.getLogger(BeanMoulder.class);
0099:
0100: /**
0101: * Display the properties of this JavaBean. If this bean has properties that implement
0102: * either DomainDAO or DomainDAO[], then also print this objects too.
0103: * @param source Javabean who's contents should be printed
0104: * @return multi-line string of this beans properties and their values
0105: */
0106: public static String printBean(Object source) {
0107: return printBean(source, null);
0108: }
0109:
0110: /**
0111: * Same as printBean(Object source), except the objectStack lists all the parent
0112: * objects its printed, and if this is one of them, it stops. This allows detection
0113: * of possible infinite recusion.
0114: * @param source Javabean who's contents should be printed
0115: * @param objectStack List of objects already traversed
0116: * @return multi-line string of this beans properties and their values
0117: */
0118: public static String printBean(Object source, List objectStack) {
0119: if (source == null)
0120: return null;
0121:
0122: // Prevent infinite object recursion
0123: if (objectStack != null)
0124: if (objectStack.contains(source))
0125: return "Object Already Used. "
0126: + source.getClass().getName() + "@"
0127: + source.hashCode();
0128: else
0129: objectStack.add(source);
0130: else {
0131: objectStack = new ArrayList();
0132: objectStack.add(source);
0133: }
0134:
0135: StringBuffer out = new StringBuffer();
0136: out.append(source.getClass().getName());
0137: out.append("\n");
0138:
0139: try {
0140: BeanInfo sInfo = Introspector
0141: .getBeanInfo(source.getClass());
0142: PropertyDescriptor[] sDescriptors = sInfo
0143: .getPropertyDescriptors();
0144: if (sDescriptors != null && sDescriptors.length != 0)
0145: for (int i = 0; i < sDescriptors.length; i++) {
0146: PropertyDescriptor sDesc = sDescriptors[i];
0147: Method sm = sDesc.getReadMethod();
0148: if (sm != null && sDesc.getWriteMethod() != null) {
0149: if (!sm.isAccessible())
0150: sm.setAccessible(true);
0151: Object sValue = sm.invoke(source, null);
0152:
0153: out.append(" ");
0154: out.append(sDesc.getName());
0155: if (source instanceof DomainDAO) {
0156: if (((DomainDAO) source).hasChanged(sDesc
0157: .getName()))
0158: out.append("*");
0159: }
0160: out.append("=");
0161: if (sValue == null)
0162: out.append("<--NULL-->\n");
0163: else if (sm.getReturnType().isArray()) {
0164: StringBuffer out2 = new StringBuffer();
0165: out2.append("Array of ");
0166: out2.append(sm.getReturnType()
0167: .getComponentType().getName());
0168: out2.append("\n");
0169: // Loop through array
0170: Object[] a = (Object[]) sValue;
0171: for (int j = 0; j < a.length; j++) {
0172: out2.append("[");
0173: out2.append(j);
0174: out2.append("] ");
0175: if (a[j] == null)
0176: out2.append("<--NULL-->");
0177: else if (DomainDAO.class
0178: .isAssignableFrom(a[j]
0179: .getClass()))
0180: out2.append(((DomainDAO) a[j])
0181: .toString(objectStack));
0182: else
0183: //out2.append(StringHelper.linePad(a[j].toString(), 4, " ",true));
0184: out2.append(a[j].toString());
0185: }
0186: out.append(StringHelper.linePad(out2
0187: .toString(), 4, " ", true));
0188: } else {
0189: if (DomainDAO.class.isAssignableFrom(sValue
0190: .getClass()))
0191: out.append(StringHelper.linePad(
0192: ((DomainDAO) sValue)
0193: .toString(objectStack),
0194: 4, " ", true));
0195: else {
0196: out.append(StringHelper.linePad(sValue
0197: .toString(), 4, " ", true));
0198: out.append("\n");
0199: }
0200:
0201: }
0202: }
0203: }
0204: } catch (IllegalAccessException e) {
0205: MouldException me = new MouldException(
0206: MouldException.ACCESS_ERROR, "???", e.getMessage());
0207: log.error(me.getLocalizedMessage(), e);
0208: //throw me;
0209: } catch (InvocationTargetException e) {
0210: MouldException me = new MouldException(
0211: MouldException.INVOCATION_ERROR, "???", e);
0212: log.error(me.getLocalizedMessage(), me.getCause());
0213: //throw me;
0214: } catch (IntrospectionException e) {
0215: MouldException me = new MouldException(
0216: MouldException.INTROSPECT_ERROR, "???", e
0217: .getMessage());
0218: log.error(me.getLocalizedMessage(), e);
0219: //throw me;
0220: }
0221: return out.toString();
0222: }
0223:
0224: /**
0225: * Mould data from domain object and its related objects into a new JavaBean based
0226: * domain object graph, based on the defined mapping rules.
0227: *<p>
0228: * Same as {@link moldFromDomain(Object source, Object target, GraphMapping graph, MappingFilter filter, String objectPath, boolean includeKeys}
0229: * except the graph is derived from the target object, a default MappingFilter (that
0230: * just returns fields from the root object) and includeKeys is false.
0231: * The objectPath is also null, assuming this is the first object in the domain
0232: * object graph.
0233: *<p>
0234: * @param source Source object to mould data from, typically extends Persistent
0235: * @param target Target object to mould data to, typically extends DomainDAO
0236: * @throws ApplicationExceptions Thrown if one or more application logic errors are generated during moulding
0237: * @throws FrameworkException Thrown if any runtime moulding error has occured.
0238: */
0239: public static void moldFromDomain(Object source, Object target)
0240: throws ApplicationExceptions, FrameworkException {
0241: moldFromDomain(source, target, null, null, null, false);
0242: }
0243:
0244: /**
0245: * Mould data from domain object and its related objects into a new JavaBean based
0246: * domain object graph, based on the defined mapping rules.
0247: *<p>
0248: * Same as {@link moldFromDomain(Object source, Object target, GraphMapping graph, MappingFilter filter, String objectPath, boolean includeKeys}
0249: * except the graph is derived from the target object, and includeKeys is false.
0250: *<p>
0251: * @param source Source object to mould data from, typically extends Persistent
0252: * @param target Target object to mould data to, typically extends DomainDAO
0253: * @param filter Filter object that it is used to control what fields are populated or the target objects
0254: * @param objectPath The path of this object being processed. This identifies possible parent
0255: * and/or indexed entries where this object is contained.
0256: * @throws ApplicationExceptions Thrown if one or more application logic errors are generated during moulding
0257: * @throws FrameworkException Thrown if any runtime moulding error has occured.
0258: */
0259: public static void moldFromDomain(Object source, Object target,
0260: MappingFilter filter, String objectPath)
0261: throws ApplicationExceptions, FrameworkException {
0262: moldFromDomain(source, target, null, filter, objectPath, false);
0263: }
0264:
0265: /**
0266: * Mould data from domain object and its related objects into a new JavaBean based
0267: * domain object graph, based on the defined mapping rules.
0268: * @param source Source object to mould data from, typically extends Persistent
0269: * @param target Target object to mould data to, typically extends DomainDAO
0270: * @param graph The mapping class with the rules of how to map this source object
0271: * @param filter Filter object that it is used to control what fields are populated or the target objects
0272: * @param objectPath The path of this object being processed. This identifies possible parent
0273: * and/or indexed entries where this object is contained.
0274: * @param includeKeys true if key fields should be included in results regardless of the filters
0275: * @throws ApplicationExceptions Thrown if one or more application logic errors are generated during moulding
0276: * @throws FrameworkException Thrown if any runtime moulding error has occured.
0277: */
0278: public static void moldFromDomain(Object source, Object target,
0279: GraphMapping graph, MappingFilter filter,
0280: String objectPath, boolean includeKeys)
0281: throws ApplicationExceptions, FrameworkException {
0282: if (graph == null)
0283: graph = MappingFactory.getInstance(target);
0284: //throw new InstantiationException("A GraphMapping must be supplied");
0285: if (filter == null)
0286: filter = new MappingFilter(graph);
0287:
0288: try {
0289: // get list of target fileds to populate
0290: String[] tFields = graph.getDataFieldNames();
0291: if (tFields != null && tFields.length != 0)
0292: for (int i = 0; i < tFields.length; i++) {
0293: // Try to map a source to a target
0294: String tName = tFields[i];
0295: String fullName = tName;
0296: if (objectPath != null)
0297: fullName = objectPath + "." + fullName;
0298:
0299: if (filter == null
0300: || filter.isFieldIncluded(fullName)
0301: || (includeKeys && graph.isKeyField(tName))) {
0302: String sName = graph.getDomainFieldName(tName);
0303: PropertyDescriptor tDesc = graph
0304: .getDataFieldDescriptor(tName);
0305: PropertyDescriptor sDesc = graph
0306: .getDomainFieldDescriptor(tName);
0307: // Based on validation in GraphMapping, that there is a
0308: // DAO descriptor with a setter, and a DO descriptor with a getter
0309: if (sDesc == null)
0310: log.error("No Getter for " + tName
0311: + " in path " + fullName);
0312:
0313: // incase getter is not public, make it available
0314: Method sm = sDesc.getReadMethod();
0315: if (!sm.isAccessible())
0316: sm.setAccessible(true);
0317:
0318: // get the setter, and make is available if needed
0319: Method tm = tDesc.getWriteMethod();
0320: if (!tm.isAccessible())
0321: tm.setAccessible(true);
0322:
0323: // Set the value if the source and target are the same datatype
0324: Class tClass = tDesc.getPropertyType();
0325: Class sClass = sDesc.getPropertyType();
0326: if (tClass.isAssignableFrom(sClass)) {
0327: // Get the value being copied
0328: Object sValue = sm.invoke(source, null);
0329: if (sValue != null) {
0330: tm.invoke(target,
0331: new Object[] { sValue });
0332: log.debug("Set " + tName + " = "
0333: + sValue);
0334: } else
0335: log
0336: .debug(tName
0337: + " no set, NULL value");
0338:
0339: // See if there is a datatype mapper for these classes
0340: } else if (DataTypeMapping.isMappable(sClass,
0341: tClass)) {
0342: // Get the value being copied
0343: Object sValue = sm.invoke(source, null);
0344: if (sValue != null) {
0345: sValue = DataTypeMapping.map(sValue,
0346: tClass);
0347: tm.invoke(target,
0348: new Object[] { sValue });
0349: log.debug("Set " + tName + " = "
0350: + sValue);
0351: } else
0352: log
0353: .debug(tName
0354: + " no set, NULL value");
0355:
0356: // See if target is a DAO, this could be a foreign object or one-to-one relationship...
0357: } else if (DomainDAO.class
0358: .isAssignableFrom(tClass)
0359: && IPersistent.class
0360: .isAssignableFrom(sClass)) {
0361: // Get the mapper for the related DAO, if it has keys, it must be a foriegn object
0362: if (graph.isForeignField(tName)) {
0363: // look at foreign key fields, and make sure they are not null
0364: List foreignKeys = graph
0365: .getForeignKeys(tName);
0366: List foreignKeyValues = new ArrayList();
0367: boolean nullKey = false;
0368: for (Iterator k = foreignKeys
0369: .iterator(); k.hasNext();) {
0370: String doProp = (String) k.next();
0371: Object value = null;
0372: PropertyDescriptor doPd = graph
0373: .getRealDomainFieldDescriptor(doProp);
0374: if (doPd != null
0375: && doPd.getReadMethod() != null) {
0376: Method m = doPd.getReadMethod();
0377: if (!m.isAccessible())
0378: m.setAccessible(true);
0379: value = m.invoke(source,
0380: new Object[] {});
0381: if (value == null)
0382: nullKey = true;
0383: foreignKeyValues.add(value);
0384: } else {
0385: throw new MouldException(
0386: MouldException.INVALID_FK_MAPPING,
0387: objectPath,
0388: doProp,
0389: graph
0390: .getDomainClassShortName());
0391: }
0392: }
0393: if (nullKey) {
0394: log
0395: .debug("Did not create skeleton object '"
0396: + tClass.getName()
0397: + "': one or more foreign key values missing.");
0398: } else {
0399: // Create the foreign object
0400: log
0401: .debug("Creating foreign object - "
0402: + tClass.getName());
0403: Object newDAO = newDAO(tClass);
0404: // Only retrieve related domain object and introspect if need
0405: if (filter
0406: .areSubFieldsIncluded(fullName)) {
0407: // read object and introspect all
0408: log
0409: .debug("Read foreign object '"
0410: + fullName
0411: + "' and mold");
0412: Object sValue = sm.invoke(
0413: source, null);
0414: if (sValue != null) {
0415: BeanMoulder.moldFromDomain(
0416: sValue, newDAO,
0417: null, filter,
0418: fullName, true);
0419: } else {
0420: log
0421: .warn("All foreign keys present, but read on foreign object returned NULL!");
0422: }
0423: } else {
0424: // just set foreign keys from current object
0425: log
0426: .debug("Set keys on skeleton foreign object only");
0427: GraphMapping graph2 = MappingFactory
0428: .getInstance(newDAO);
0429: Set keys = graph2
0430: .getKeyFields();
0431: if (keys == null
0432: || keys.size() != foreignKeyValues
0433: .size()) {
0434: throw new MouldException(
0435: MouldException.MISMATCH_FK_MAPPING,
0436: objectPath, target
0437: .getClass()
0438: .getName(),
0439: newDAO.getClass()
0440: .getName());
0441: }
0442: int k2 = 0;
0443: // Look through al the foreign keys on the skeleton object
0444: for (Iterator k = keys
0445: .iterator(); k
0446: .hasNext(); k2++) {
0447: String keyField = (String) k
0448: .next();
0449: Object keyValue = foreignKeyValues
0450: .get(k2);
0451: PropertyDescriptor pd = graph2
0452: .getDataFieldDescriptor(keyField);
0453: if (pd != null
0454: && pd
0455: .getWriteMethod() != null) {
0456: Method m = pd
0457: .getWriteMethod();
0458: if (!m.isAccessible())
0459: m
0460: .setAccessible(true);
0461: m
0462: .invoke(
0463: newDAO,
0464: new Object[] { keyValue });
0465: } else {
0466: throw new MouldException(
0467: MouldException.CANT_SET_KEY_FIELD,
0468: objectPath,
0469: keyField,
0470: newDAO
0471: .getClass()
0472: .getName());
0473: }
0474: }
0475: }
0476: tm.invoke(target,
0477: new Object[] { newDAO });
0478: log.debug("Set " + tName + " = "
0479: + newDAO);
0480: }
0481: } else {
0482: // This is not a foreign object, must be a related object
0483: if (filter
0484: .areSubFieldsIncluded(fullName)) {
0485: // Create the related object
0486: log
0487: .debug("Creating One-To-One object - "
0488: + tClass.getName());
0489: Object newDAO = newDAO(tClass);
0490: // read object and introspect all
0491: log.debug("Read related object '"
0492: + fullName + "' and mold");
0493: Object sValue = sm.invoke(source,
0494: null);
0495: if (sValue != null) {
0496: BeanMoulder.moldFromDomain(
0497: sValue, newDAO, null,
0498: filter, fullName, true);
0499: } else {
0500: log
0501: .debug("Related object '"
0502: + fullName
0503: + "' not found. Ignore it!");
0504: }
0505: tm.invoke(target,
0506: new Object[] { newDAO });
0507: log.debug("Set " + tName + " = "
0508: + newDAO);
0509: } else
0510: log
0511: .debug("No subfields for object "
0512: + fullName
0513: + " included. Object not retrieved");
0514: }//END-related object
0515:
0516: // See if Target may be an array of DAO's
0517: } else if (tClass.isArray()
0518: && DomainDAO.class
0519: .isAssignableFrom(tClass
0520: .getComponentType())
0521: && filter
0522: .areSubFieldsIncluded(fullName)) {
0523: log.debug("Target is an array of DAO's");
0524: log.debug("Read related objects '"
0525: + fullName + "' and mold");
0526: Object sValue = sm.invoke(source, null);
0527: if (sClass.isArray()
0528: && IPersistent.class
0529: .isAssignableFrom(sClass
0530: .getComponentType())) {
0531: log
0532: .debug("Source is an array of Persistent Objects");
0533: Object[] sArray = (Object[]) sValue;
0534: if (sArray.length > 0) {
0535: Object[] tArray = (Object[]) Array
0536: .newInstance(
0537: tClass
0538: .getComponentType(),
0539: sArray.length);
0540: log
0541: .debug("Translate Array of Size "
0542: + sArray.length);
0543: for (int j = 0; j < sArray.length; j++) {
0544: Object newDAO = newDAO(tClass
0545: .getComponentType());
0546: BeanMoulder.moldFromDomain(
0547: sArray[j], newDAO,
0548: null, filter, fullName,
0549: false);
0550: tArray[j] = newDAO;
0551: log.debug("Add to array [" + j
0552: + "] : " + newDAO);
0553: }
0554: tm
0555: .invoke(
0556: target,
0557: new Object[] { (Object) tArray });
0558: log.debug("Set Array " + tName);
0559: } else
0560: log
0561: .debug("Source Array is empty! Do Nothing");
0562: } // source is DO array
0563:
0564: // Error... No way to map property
0565: } else {
0566: String err = "Can't Mold Property "
0567: + fullName + " from "
0568: + sClass.getName() + " to "
0569: + tClass.getName();
0570: log.error(err);
0571: throw new RuntimeException(err);
0572: }
0573: } // is included in filtered fields
0574: }
0575:
0576: // Clear changed fields on updated DAO
0577: if (target != null && target instanceof DomainDAO)
0578: ((DomainDAO) target).clearChanges();
0579:
0580: } catch (IllegalAccessException e) {
0581: MouldException me = new MouldException(
0582: MouldException.ACCESS_ERROR, objectPath, e
0583: .getMessage());
0584: log.error(me.getLocalizedMessage(), e);
0585: throw me;
0586: } catch (InvocationTargetException e) {
0587: if (e.getCause() != null) {
0588: if (e.getCause() instanceof FrameworkException)
0589: throw (FrameworkException) e.getCause();
0590: if (e.getCause() instanceof ApplicationExceptions)
0591: throw (ApplicationExceptions) e.getCause();
0592: if (e.getCause() instanceof ApplicationException) {
0593: ApplicationExceptions aes = new ApplicationExceptions();
0594: aes.add((ApplicationException) e.getCause());
0595: throw aes;
0596: }
0597: }
0598: MouldException me = new MouldException(
0599: MouldException.INVOCATION_ERROR, objectPath, e);
0600: log.error(me.getLocalizedMessage(), me.getCause());
0601: throw me;
0602: } catch (InstantiationException e) {
0603: MouldException me = new MouldException(
0604: MouldException.INSTANTICATION_ERROR, objectPath, e
0605: .getMessage());
0606: log.error(me.getLocalizedMessage(), e);
0607: throw me;
0608: }
0609: }
0610:
0611: private static Object newDAO(Class clazz)
0612: throws InstantiationException {
0613: try {
0614: Constructor c = clazz.getConstructor(new Class[] {});
0615: if (c == null)
0616: throw new InstantiationException(
0617: "No zero argument construtor found");
0618: Object dao = c.newInstance((Object[]) null);
0619: log.debug("Created Object : " + dao);
0620: return dao;
0621: } catch (InstantiationException e) {
0622: throw e;
0623: } catch (Exception e) {
0624: log.error("Can't create DAO object - " + e.getMessage(), e);
0625: throw new InstantiationException(e.getMessage());
0626: }
0627: }
0628:
0629: /**
0630: * Take a source object and try and mold it back it its domain object
0631: * @param path The path of this object being processed. This identifies possible parent
0632: * and/or indexed entries where this object is contained.
0633: * @param source Source object to mould from, typically a DomainDAO
0634: * @param uow Transaction handle all creates/update will be performed within.
0635: * Throws an exception if null.
0636: * @param handler Possible bean handler to be used when processing this source object graph
0637: * @throws ApplicationExceptions Thrown if one or more application logic errors are generated during moulding
0638: * @throws FrameworkException Thrown if any runtime moulding error has occured.
0639: */
0640: public static void updateBean(String path, DomainDAO source,
0641: UOW uow, MouldHandler handler)
0642: throws ApplicationExceptions, FrameworkException {
0643: log.debug("Update Bean " + path);
0644: // Call custom validation code in the DAO
0645: source.validate();
0646: ApplicationExceptions aes = new ApplicationExceptions();
0647: if (uow == null) {
0648: String err = "UOW Required";
0649: log.error(err);
0650: throw new RuntimeException(err);
0651: }
0652:
0653: try {
0654: IPersistent domainObject = null;
0655: GraphMapping mapping = MappingFactory.getInstance(source);
0656: Map keys = new LinkedHashMap();
0657: Class doClass = mapping.getDomainClass();
0658:
0659: // Get the key fields used in the domain object
0660: boolean gotKeys = fillInKeys(path, source, mapping, keys);
0661:
0662: // read DO based on key
0663: if (gotKeys) {
0664: // get the method on the DO to read via PK
0665: Method[] ma = doClass.getMethods();
0666: Method findByPK = null;
0667: for (int i = 0; i < ma.length; i++) {
0668: if (ma[i].getName().equals("findByPK")) {
0669: if (ma[i].getParameterTypes().length == (keys
0670: .size() + 1)
0671: && (ma[i].getParameterTypes())[0] == UOW.class) {
0672: // Found with name and correct no. of input params
0673: findByPK = ma[i];
0674: break;
0675: }
0676: }
0677: }
0678: if (findByPK == null) {
0679: aes.add(new DomainObjectNotFoundException(doClass
0680: .getName()
0681: + " @ " + path));
0682: throw aes;
0683: }
0684:
0685: // Build input array
0686: Object[] inputs = new Object[keys.size() + 1];
0687: {
0688: inputs[0] = uow;
0689: int i = 1;
0690: for (Iterator it = keys.values().iterator(); it
0691: .hasNext(); i++) {
0692: inputs[i] = it.next();
0693: }
0694: }
0695:
0696: // Find Object based on key
0697: domainObject = (IPersistent) findByPK.invoke(null,
0698: inputs);
0699:
0700: } else
0701: log
0702: .debug("Object "
0703: + path
0704: + " has either missing or null key values - Assume Create is needed");
0705:
0706: // Create object if not found
0707: if (domainObject == null) {
0708: // NEW OBJECT, create and reflect keys
0709: log.debug("DO '" + mapping.getDomainClassShortName()
0710: + "' not found with key, create a new one...");
0711: domainObject = (IPersistent) doClass.newInstance();
0712: // set the key fields
0713: for (Iterator it = keys.keySet().iterator(); it
0714: .hasNext();) {
0715: String keyField = (String) it.next();
0716: Object value = keys.get(keyField);
0717: updateProperty(mapping
0718: .getDomainFieldDescriptor(keyField), value,
0719: domainObject);
0720: }
0721: } else {
0722: log.debug("Found DO '"
0723: + mapping.getDomainClassShortName()
0724: + "' with key,");
0725: }
0726:
0727: // Now update all domain fields
0728: updateBeanData(path, source, uow, handler, mapping,
0729: domainObject);
0730:
0731: } catch (IllegalAccessException e) {
0732: MouldException me = new MouldException(
0733: MouldException.ACCESS_ERROR, path, e.getMessage());
0734: log.error(me.getLocalizedMessage(), e);
0735: throw me;
0736: } catch (InvocationTargetException e) {
0737: if (e.getCause() != null) {
0738: if (e.getCause() instanceof FrameworkException)
0739: throw (FrameworkException) e.getCause();
0740: if (e.getCause() instanceof ApplicationExceptions)
0741: throw (ApplicationExceptions) e.getCause();
0742: if (e.getCause() instanceof ApplicationException) {
0743: aes.add((ApplicationException) e.getCause());
0744: throw aes;
0745: }
0746: }
0747: MouldException me = new MouldException(
0748: MouldException.INVOCATION_ERROR, path, e);
0749: log.error(me.getLocalizedMessage(), me.getCause());
0750: throw me;
0751: } catch (InstantiationException e) {
0752: MouldException me = new MouldException(
0753: MouldException.INSTANTICATION_ERROR, path, e
0754: .getMessage());
0755: log.error(me.getLocalizedMessage(), e);
0756: throw me;
0757: }
0758: }
0759:
0760: /**
0761: * Take a source object and delete it or delete is children if it has any
0762: * @param path The path of this object being processed. This identifies possible parent
0763: * and/or indexed entries where this object is contained.
0764: * @param source Source object to mould from, typically a DomainDAO
0765: * @param uow Transaction handle all creates/update will be performed within.
0766: * Throws an exception if null.
0767: * @param handler Possible bean handler to be used when processing this source object graph
0768: * @throws ApplicationExceptions Thrown if one or more application logic errors are generated during moulding
0769: * @throws FrameworkException Thrown if any runtime moulding error has occured.
0770: */
0771: public static void deleteBean(String path, DomainDAO source,
0772: UOW uow, MouldHandler handler)
0773: throws ApplicationExceptions, FrameworkException {
0774: log.debug("Delete Bean " + path);
0775: // Call custom validation code in the DAO
0776: source.validate();
0777: ApplicationExceptions aes = new ApplicationExceptions();
0778: if (uow == null) {
0779: String err = "UOW Required";
0780: log.error(err);
0781: throw new RuntimeException(err);
0782: }
0783:
0784: try {
0785: IPersistent domainObject = null;
0786: GraphMapping mapping = MappingFactory.getInstance(source);
0787: Map keys = new LinkedHashMap();
0788: Class doClass = mapping.getDomainClass();
0789:
0790: // Get the key fields used in the domain object
0791: boolean gotKeys = fillInKeys(path, source, mapping, keys);
0792:
0793: //----------------------------------------------------------------
0794: // read DO based on key
0795: if (gotKeys) {
0796: // get the method on the DO to read via PK
0797: Method[] ma = doClass.getMethods();
0798: Method findByPK = null;
0799: for (int i = 0; i < ma.length; i++) {
0800: if (ma[i].getName().equals("findByPK")) {
0801: if (ma[i].getParameterTypes().length == (keys
0802: .size() + 1)
0803: && (ma[i].getParameterTypes())[0] == UOW.class) {
0804: // Found with name and correct no. of input params
0805: findByPK = ma[i];
0806: break;
0807: }
0808: }
0809: }
0810: if (findByPK == null) {
0811: aes.add(new DomainObjectNotFoundException(doClass
0812: .getName()));
0813: throw aes;
0814: }
0815:
0816: // Build input array
0817: Object[] inputs = new Object[keys.size() + 1];
0818: {
0819: inputs[0] = uow;
0820: int i = 1;
0821: for (Iterator it = keys.values().iterator(); it
0822: .hasNext(); i++) {
0823: inputs[i] = it.next();
0824: }
0825: }
0826:
0827: // Find Object based on key
0828: domainObject = (IPersistent) findByPK.invoke(null,
0829: inputs);
0830:
0831: } else
0832: log
0833: .debug("Object "
0834: + path
0835: + " has either missing or null key values - Assume Create is needed");
0836: // Error if DO not found
0837: if (domainObject == null) {
0838: String label = doClass.getName();
0839: // Try and use meta data to get domain objects label
0840: try {
0841: label = PersistentHelper.getLabelToken(doClass
0842: .getName());
0843: } catch (Exception e) {
0844: // ignore any problem trying to get the label!
0845: }
0846: aes.add(new DomainObjectNotFoundException(label
0847: + " (path=" + path + ")"));
0848: throw aes;
0849: }
0850:
0851: // Process the delete, either on this DO, or a related DO if there is one
0852: deleteBeanData(path, source, uow, handler, mapping,
0853: domainObject);
0854:
0855: } catch (IllegalAccessException e) {
0856: MouldException me = new MouldException(
0857: MouldException.ACCESS_ERROR, path, e.getMessage());
0858: log.error(me.getLocalizedMessage(), e);
0859: throw me;
0860: } catch (InvocationTargetException e) {
0861: if (e.getCause() != null) {
0862: if (e.getCause() instanceof FrameworkException)
0863: throw (FrameworkException) e.getCause();
0864: if (e.getCause() instanceof ApplicationExceptions)
0865: throw (ApplicationExceptions) e.getCause();
0866: if (e.getCause() instanceof ApplicationException) {
0867: aes.add((ApplicationException) e.getCause());
0868: throw aes;
0869: }
0870: }
0871: MouldException me = new MouldException(
0872: MouldException.INVOCATION_ERROR, path, e);
0873: log.error(me.getLocalizedMessage(), me.getCause());
0874: throw me;
0875: } catch (InstantiationException e) {
0876: MouldException me = new MouldException(
0877: MouldException.INSTANTICATION_ERROR, path, e
0878: .getMessage());
0879: log.error(me.getLocalizedMessage(), e);
0880: throw me;
0881: }
0882: // } catch (Exception e) {
0883: // throw handleException(e,aes);
0884: // }
0885: }
0886:
0887: /** Pass in an emty map and it fills it with Key = Value for the source
0888: * object. It returns false if one or more key values are null, or if this
0889: * object has no keys defined
0890: */
0891: private static boolean fillInKeys(String path, DomainDAO source,
0892: GraphMapping mapping, Map map)
0893: throws InvocationTargetException, MouldException {
0894: try {
0895: Set keys = mapping.getKeyFields();
0896: boolean nullKey = false;
0897: if (keys == null || keys.size() == 0) {
0898: log.debug("Object Has No KEYS! - "
0899: + source.getClass().getName());
0900: return false;
0901: }
0902: // Loop through all the keys get het the values
0903: for (Iterator k = keys.iterator(); k.hasNext();) {
0904: String keyField = (String) k.next();
0905: PropertyDescriptor pd = mapping
0906: .getDataFieldDescriptor(keyField);
0907: if (pd != null && pd.getReadMethod() != null) {
0908: Method m = pd.getReadMethod();
0909: if (!m.isAccessible())
0910: m.setAccessible(true);
0911: Object value = m.invoke(source, new Object[] {});
0912: map.put(keyField, value);
0913: log.debug("Key " + keyField + "='" + value
0914: + "' on object '"
0915: + source.getClass().getName() + "'");
0916: if (value == null) {
0917: nullKey = true;
0918: }
0919: } else {
0920: MouldException me = new MouldException(
0921: MouldException.NO_KEY_ON_OBJECT, path,
0922: keyField, source.getClass().getName());
0923: log.error(me.getLocalizedMessage());
0924: throw me;
0925: }
0926: }
0927: return !nullKey;
0928: } catch (IllegalAccessException e) {
0929: MouldException me = new MouldException(
0930: MouldException.ACCESS_ERROR, path, e.getMessage());
0931: log.error(me.getLocalizedMessage(), e);
0932: throw me;
0933: // } catch (InvocationTargetException e) {
0934: // MouldException me = new MouldException(MouldException.INVOCATION_ERROR, path, e );
0935: // log.error(me.getLocalizedMessage(),me.getCause());
0936: // throw me;
0937: }
0938: }
0939:
0940: private static void updateBeanData(String path, DomainDAO source,
0941: UOW uow, MouldHandler handler, GraphMapping mapping,
0942: IPersistent domainObject) throws InstantiationException,
0943: IllegalAccessException, InvocationTargetException,
0944: ApplicationExceptions, FrameworkException {
0945:
0946: try {
0947: //----------------------------------------------------------------
0948: // Fire 'startBean' handler
0949: if (handler != null)
0950: handler.startBean(path, source, domainObject);
0951:
0952: //----------------------------------------------------------------
0953: // Reflect all normal fields
0954: for (Iterator it = mapping.getFields().iterator(); it
0955: .hasNext();) {
0956: String field = (String) it.next();
0957: if (source.hasChanged(field)) {
0958: Object value = getProperty(mapping
0959: .getDataFieldDescriptor(field), source);
0960: //if(value!=null)
0961: updateProperty(mapping
0962: .getDomainFieldDescriptor(field), value,
0963: domainObject);
0964: }
0965: }
0966:
0967: //----------------------------------------------------------------
0968: // Reflect any foreign keys
0969: for (Iterator it = mapping.getForeignFields().iterator(); it
0970: .hasNext();) {
0971: String field = (String) it.next();
0972: if (source.hasChanged(field)) {
0973: Object value = getProperty(mapping
0974: .getDataFieldDescriptor(field), source);
0975: if (value != null) {
0976: // need to map foreign keys back
0977: List targetKeys = mapping.getForeignKeys(field);
0978: GraphMapping fMapping = MappingFactory
0979: .getInstance(mapping
0980: .getDataFieldDescriptor(field)
0981: .getPropertyType());
0982: Set sourceKeys = fMapping.getKeyFields();
0983: int i = 0;
0984: for (Iterator i2 = sourceKeys.iterator(); i2
0985: .hasNext(); i++) {
0986: String sourceFld = (String) i2.next();
0987: String targetFld = (String) targetKeys
0988: .get(i);
0989: log.debug("Copy Foreign Key Field from "
0990: + sourceFld + " to " + targetFld);
0991: Object value2 = getProperty(fMapping
0992: .getDataFieldDescriptor(sourceFld),
0993: value);
0994: updateProperty(
0995: mapping
0996: .getRealDomainFieldDescriptor(targetFld),
0997: value2, domainObject);
0998: }
0999: }
1000: }
1001: }
1002:
1003: //----------------------------------------------------------------
1004: // Store Record
1005: if (domainObject.isDatabaseOccurence()) {
1006: log.debug("UOW.Update Domain Object");
1007: //----------------------------------------------------------------
1008: // Fire 'startBeanUpdate' handler
1009: if (handler != null)
1010: handler.startBeanUpdate(path, source, domainObject);
1011: uow.update(domainObject);
1012: //----------------------------------------------------------------
1013: // Fire 'endBeanUpdate' handler
1014: if (handler != null)
1015: handler.endBeanUpdate(path, source, domainObject);
1016: } else {
1017: log.debug("UOW.Add Domain Object");
1018: //----------------------------------------------------------------
1019: // Fire 'startBeanAdd' handler
1020: if (handler != null)
1021: handler.startBeanAdd(path, source, domainObject);
1022: uow.add(domainObject);
1023: //----------------------------------------------------------------
1024: // Fire 'endBeanAdd' handler
1025: if (handler != null)
1026: handler.endBeanAdd(path, source, domainObject);
1027: }
1028:
1029: //----------------------------------------------------------------
1030: // Reflect any related objects
1031: for (Iterator it = mapping.getRelatedFields().iterator(); it
1032: .hasNext();) {
1033: String field = (String) it.next();
1034: if (source.hasChanged(field)) {
1035: // Only do the update if the source object was updated!
1036: Object value = getProperty(mapping
1037: .getDataFieldDescriptor(field), source);
1038: if (value != null) {
1039: if (value.getClass().isArray()) {
1040: // The related field is an array of objects (one-to-many)
1041: Object[] values = (Object[]) value;
1042: for (int i = 0; i < values.length; i++) {
1043: DomainDAO dao = (DomainDAO) values[i]; // Assumes its a DAO....what else could it be?
1044: if (dao != null) {
1045: updateChildBean(path + "." + field
1046: + "[" + i + "]", dao, uow,
1047: handler, domainObject,
1048: mapping, field);
1049: }
1050: }
1051: } else {
1052: // Or a single Object (one-to-one)
1053: DomainDAO dao = (DomainDAO) value; // Assumes its a DAO....what else could it be?
1054: updateChildBean(path + "." + field, dao,
1055: uow, handler, domainObject,
1056: mapping, field);
1057: }
1058: }
1059: }
1060: }
1061:
1062: //----------------------------------------------------------------
1063: // Fire 'endBean' handler
1064: if (handler != null)
1065: handler.endBean(path, source, domainObject);
1066:
1067: } catch (ApplicationException e) {
1068: ApplicationExceptions aes = new ApplicationExceptions();
1069: aes.add(e);
1070: throw aes;
1071: }
1072: }
1073:
1074: /** Take a source object and try and mold it back it its domain object.
1075: * This is the same as updateParent, except from the way it retrieved the
1076: * record, and the way it creates a new record.
1077: */
1078: private static void updateChildBean(String path, DomainDAO source,
1079: UOW uow, MouldHandler handler, IPersistent parentDomain,
1080: GraphMapping parentMapping, String parentField)
1081: throws ApplicationExceptions, FrameworkException {
1082:
1083: log.debug("Update Child Bean " + path);
1084: // Call custom validation code in the DAO
1085: source.validate();
1086: ApplicationExceptions aes = new ApplicationExceptions();
1087: if (uow == null) {
1088: String err = "UOW Required";
1089: log.error(err);
1090: throw new RuntimeException(err);
1091: }
1092:
1093: String relationshipName = parentMapping
1094: .getDomainFieldName(parentField);
1095: if (relationshipName.endsWith("Array"))
1096: relationshipName = relationshipName.substring(0,
1097: relationshipName.length() - 5);
1098: if (relationshipName.endsWith("Object"))
1099: relationshipName = relationshipName.substring(0,
1100: relationshipName.length() - 6);
1101:
1102: try {
1103:
1104: IPersistent domainObject = null;
1105: GraphMapping mapping = MappingFactory.getInstance(source);
1106: Map keys = new LinkedHashMap();
1107: Class doClass = mapping.getDomainClass();
1108: boolean gotKeys = false;
1109:
1110: if (mapping.getKeyFields() == null
1111: || mapping.getKeyFields().size() == 0) {
1112: // No keys, must be one-to-one
1113: log.debug("Find 'one-to-one' object - " + path);
1114:
1115: // Just use the getXxxObject method to get the related domain object,
1116: // if there is one...
1117: domainObject = (IPersistent) getProperty(parentMapping
1118: .getDomainFieldDescriptor(parentField),
1119: parentDomain);
1120: if (domainObject == null)
1121: log.debug("Not Found - " + path);
1122: } else {
1123: // Get the key fields used in the domain object. Use the findXxxxxCriteria() method,
1124: // then add the extra fields to the criteria object, to get the unique record.
1125: gotKeys = fillInKeys(path, source, mapping, keys);
1126:
1127: // read DO based on key
1128: if (gotKeys) {
1129:
1130: // get the method to get the PK criteria (i.e. public Criteria findVendorSiteCriteria(); )
1131: Method findCriteria = null;
1132: String methodName = "find"
1133: + StringHelper.getUpper1(relationshipName)
1134: + "Criteria";
1135: try {
1136: findCriteria = parentDomain.getClass()
1137: .getMethod(methodName, new Class[] {});
1138: } catch (NoSuchMethodException e) {
1139: log.error("Find method '" + methodName
1140: + "' not found!");
1141: }
1142: if (findCriteria == null) {
1143: throw new MouldException(
1144: MouldException.METHOD_NOT_FOUND, path,
1145: methodName);
1146: }
1147:
1148: // Find Criteria For Related Object
1149: Criteria criteria = (Criteria) findCriteria.invoke(
1150: parentDomain, new Object[] {});
1151: // Add extra key info...
1152: for (Iterator it = keys.keySet().iterator(); it
1153: .hasNext();) {
1154: String keyField = (String) it.next();
1155: Object value = keys.get(keyField);
1156: keyField = StringHelper.getUpper1(mapping
1157: .getDomainFieldName(keyField));
1158: criteria.addCriteria(keyField, value);
1159: log.debug(path + "- Add to criteria:"
1160: + keyField + "=" + value);
1161: }
1162: // See if we get an object :-)
1163: Iterator itr = uow.query(criteria).iterator();
1164: if (itr.hasNext())
1165: domainObject = (IPersistent) itr.next();
1166: if (itr.hasNext()) {
1167: // Error, multiple objects found
1168: MultipleDomainObjectsFoundException m = new MultipleDomainObjectsFoundException(
1169: criteria.getTable() + " @ " + path);
1170: aes.add(m);
1171: throw aes;
1172: }
1173:
1174: } else {
1175: log
1176: .debug("Object "
1177: + path
1178: + " has either missing or null key values - Assume Create is needed");
1179: }
1180: }
1181:
1182: // Create object if not found
1183: if (domainObject == null) {
1184: // NEW OBJECT, create and reflect keys
1185: log.debug("DO '" + mapping.getDomainClassShortName()
1186: + "' not found with key, create a new one...");
1187: // find method on parent used to create object
1188: Method newObject = null;
1189: String methodName = "new"
1190: + StringHelper.getUpper1(relationshipName)
1191: + "Object";
1192: try {
1193: newObject = parentDomain.getClass().getMethod(
1194: methodName, new Class[] {});
1195: } catch (NoSuchMethodException e) {
1196: log.error("Method '" + methodName
1197: + "()' not found!");
1198: }
1199: if (newObject == null)
1200: throw new MouldException(
1201: MouldException.METHOD_NOT_FOUND, path,
1202: methodName);
1203:
1204: // Call method to create object
1205: domainObject = (IPersistent) newObject.invoke(
1206: parentDomain, new Object[] {});
1207:
1208: // Set the key fields
1209: for (Iterator it = keys.keySet().iterator(); it
1210: .hasNext();) {
1211: String keyField = (String) it.next();
1212: Object value = keys.get(keyField);
1213: updateProperty(mapping
1214: .getDomainFieldDescriptor(keyField), value,
1215: domainObject);
1216: }
1217: } else {
1218: log.debug("Found DO '"
1219: + mapping.getDomainClassShortName()
1220: + "' with key,");
1221: }
1222:
1223: // Now update all domain fields
1224: updateBeanData(path, source, uow, handler, mapping,
1225: domainObject);
1226:
1227: } catch (IllegalAccessException e) {
1228: MouldException me = new MouldException(
1229: MouldException.ACCESS_ERROR, path, e.getMessage());
1230: log.error(me.getLocalizedMessage(), e);
1231: throw me;
1232: } catch (InvocationTargetException e) {
1233: if (e.getCause() != null) {
1234: if (e.getCause() instanceof FrameworkException)
1235: throw (FrameworkException) e.getCause();
1236: if (e.getCause() instanceof ApplicationExceptions)
1237: throw (ApplicationExceptions) e.getCause();
1238: if (e.getCause() instanceof ApplicationException) {
1239: aes.add((ApplicationException) e.getCause());
1240: throw aes;
1241: }
1242: }
1243: MouldException me = new MouldException(
1244: MouldException.INVOCATION_ERROR, path, e);
1245: log.error(me.getLocalizedMessage(), me.getCause());
1246: throw me;
1247: } catch (InstantiationException e) {
1248: MouldException me = new MouldException(
1249: MouldException.INSTANTICATION_ERROR, path, e
1250: .getMessage());
1251: log.error(me.getLocalizedMessage(), e);
1252: throw me;
1253: }
1254: }
1255:
1256: /** Take a source object and try and mold it back it its domain object.
1257: * This is the same as updateParent, except from the way it retrieved the
1258: * record, and the way it creates a new record.
1259: */
1260: private static void deleteChildBean(String path, DomainDAO source,
1261: UOW uow, MouldHandler handler, IPersistent parentDomain,
1262: GraphMapping parentMapping, String parentField)
1263: throws ApplicationExceptions, FrameworkException {
1264:
1265: log.debug("Delete Child Bean " + path);
1266: // Call custom validation code in the DAO
1267: source.validate();
1268: ApplicationExceptions aes = new ApplicationExceptions();
1269: if (uow == null) {
1270: String err = "UOW Required";
1271: log.error(err);
1272: throw new RuntimeException(err);
1273: }
1274:
1275: String relationshipName = parentMapping
1276: .getDomainFieldName(parentField);
1277: if (relationshipName.endsWith("Array"))
1278: relationshipName = relationshipName.substring(0,
1279: relationshipName.length() - 5);
1280: if (relationshipName.endsWith("Object"))
1281: relationshipName = relationshipName.substring(0,
1282: relationshipName.length() - 6);
1283:
1284: try {
1285:
1286: IPersistent domainObject = null;
1287: GraphMapping mapping = MappingFactory.getInstance(source);
1288: Map keys = new LinkedHashMap();
1289: Class doClass = mapping.getDomainClass();
1290: boolean gotKeys = false;
1291:
1292: if (mapping.getKeyFields() == null
1293: || mapping.getKeyFields().size() == 0) {
1294: // No keys, must be one-to-one
1295: log.debug("Find 'one-to-one' object - " + path);
1296:
1297: // Just use the getXxxObject method to get the related domain object,
1298: // if there is one...
1299: domainObject = (IPersistent) getProperty(parentMapping
1300: .getDomainFieldDescriptor(parentField),
1301: parentDomain);
1302: if (domainObject == null)
1303: log.debug("Not Found - " + path);
1304: } else {
1305: // Get the key fields used in the domain object. Use the findXxxxxCriteria() method,
1306: // then add the extra fields to the criteria object, to get the unique record.
1307: gotKeys = fillInKeys(path, source, mapping, keys);
1308:
1309: // read DO based on key
1310: if (gotKeys) {
1311: // get the method to get the PK criteria (i.e. public Criteria findVendorSiteCriteria(); )
1312: Method findCriteria = null;
1313: String methodName = "find"
1314: + StringHelper.getUpper1(relationshipName)
1315: + "Criteria";
1316: try {
1317: findCriteria = parentDomain.getClass()
1318: .getMethod(methodName, new Class[] {});
1319: } catch (NoSuchMethodException e) {
1320: log.error("Find method '" + methodName
1321: + "' not found!");
1322: }
1323: if (findCriteria == null)
1324: throw new MouldException(
1325: MouldException.METHOD_NOT_FOUND, path,
1326: methodName);
1327:
1328: // Find Criteria For Related Object
1329: Criteria criteria = (Criteria) findCriteria.invoke(
1330: parentDomain, new Object[] {});
1331: // Add extra key info...
1332: for (Iterator it = keys.keySet().iterator(); it
1333: .hasNext();) {
1334: String keyField = (String) it.next();
1335: Object value = keys.get(keyField);
1336: keyField = StringHelper.getUpper1(mapping
1337: .getDomainFieldName(keyField));
1338: criteria.addCriteria(keyField, value);
1339: log.debug(path + "- Add to criteria:"
1340: + keyField + "=" + value);
1341: }
1342: // See if we get an object :-)
1343: Iterator itr = uow.query(criteria).iterator();
1344: if (itr.hasNext())
1345: domainObject = (IPersistent) itr.next();
1346: if (itr.hasNext()) {
1347: // Error, multiple objects found
1348: MultipleDomainObjectsFoundException m = new MultipleDomainObjectsFoundException(
1349: criteria.getTable() + " @ " + path);
1350: aes.add(m);
1351: throw aes;
1352: }
1353: }
1354:
1355: }
1356: // Error if DO not found
1357: if (domainObject == null) {
1358: aes.add(new DomainObjectNotFoundException(doClass
1359: .getName()
1360: + " @ " + path));
1361: throw aes;
1362: }
1363:
1364: // Process the delete, either on this DO, or a related DO if there is one
1365: deleteBeanData(path, source, uow, handler, mapping,
1366: domainObject);
1367:
1368: } catch (IllegalAccessException e) {
1369: MouldException me = new MouldException(
1370: MouldException.ACCESS_ERROR, path, e.getMessage());
1371: log.error(me.getLocalizedMessage(), e);
1372: throw me;
1373: } catch (InvocationTargetException e) {
1374: if (e.getCause() != null) {
1375: if (e.getCause() instanceof FrameworkException)
1376: throw (FrameworkException) e.getCause();
1377: if (e.getCause() instanceof ApplicationExceptions)
1378: throw (ApplicationExceptions) e.getCause();
1379: if (e.getCause() instanceof ApplicationException) {
1380: aes.add((ApplicationException) e.getCause());
1381: throw aes;
1382: }
1383: }
1384: MouldException me = new MouldException(
1385: MouldException.INVOCATION_ERROR, path, e);
1386: log.error(me.getLocalizedMessage(), me.getCause());
1387: throw me;
1388: } catch (InstantiationException e) {
1389: MouldException me = new MouldException(
1390: MouldException.INSTANTICATION_ERROR, path, e
1391: .getMessage());
1392: log.error(me.getLocalizedMessage(), e);
1393: throw me;
1394: }
1395: }
1396:
1397: private static void deleteBeanData(String path, DomainDAO source,
1398: UOW uow, MouldHandler handler, GraphMapping mapping,
1399: IPersistent domainObject) throws InstantiationException,
1400: IllegalAccessException, InvocationTargetException,
1401: ApplicationExceptions, FrameworkException {
1402: try {
1403:
1404: //----------------------------------------------------------------
1405: // Fire 'startBean' handler
1406: if (handler != null)
1407: handler.startBean(path, source, domainObject);
1408:
1409: //----------------------------------------------------------------
1410: // Now loop through children, if there is one, delete it, and leave parent alone
1411: boolean deleteChild = false;
1412:
1413: // Reflect any related objects
1414: for (Iterator it = mapping.getRelatedFields().iterator(); it
1415: .hasNext();) {
1416: String field = (String) it.next();
1417: if (source.hasChanged(field)) {
1418: Object value = getProperty(mapping
1419: .getDataFieldDescriptor(field), source);
1420: if (value != null) {
1421: if (value.getClass().isArray()) {
1422: // The related field is an array of objects (one-to-many)
1423: Object[] values = (Object[]) value;
1424: for (int i = 0; i < values.length; i++) {
1425: DomainDAO dao = (DomainDAO) values[i]; // Assumes its a DAO....what else could it be?
1426: if (dao != null) {
1427: deleteChild = true;
1428: deleteChildBean(path + "." + field
1429: + "[" + i + "]", dao, uow,
1430: handler, domainObject,
1431: mapping, field);
1432: }
1433: }
1434: } else {
1435: // The related field is a single object (one-to-many)
1436: DomainDAO dao = (DomainDAO) value; // Assumes its a DAO....what else could it be?
1437: deleteChild = true;
1438: deleteChildBean(path + "." + field, dao,
1439: uow, handler, domainObject,
1440: mapping, field);
1441: }
1442: }
1443: }
1444: }
1445:
1446: //----------------------------------------------------------------
1447: // Delete this record, as it had no children
1448: if (!deleteChild) {
1449: log.debug("UOW.Delete Domain Object");
1450: // Fire 'startBeanDelete' handler
1451: if (handler != null)
1452: handler.startBeanDelete(path, source, domainObject);
1453:
1454: uow.delete(domainObject);
1455:
1456: // Fire 'endBeanDelete' handler
1457: if (handler != null)
1458: handler.endBeanDelete(path, source, domainObject);
1459: }
1460:
1461: //----------------------------------------------------------------
1462: // Fire 'endBean' handler
1463: if (handler != null)
1464: handler.endBean(path, source, domainObject);
1465:
1466: } catch (ApplicationException e) {
1467: ApplicationExceptions aes = new ApplicationExceptions();
1468: aes.add(e);
1469: throw aes;
1470: }
1471: }
1472:
1473: private static void setProperty(PropertyDescriptor pd,
1474: Object value, Object source) throws IllegalAccessException,
1475: InvocationTargetException, MouldException {
1476: if (pd != null && pd.getWriteMethod() != null) {
1477: Method m = pd.getWriteMethod();
1478: if (!m.isAccessible())
1479: m.setAccessible(true);
1480: Class tClass = m.getParameterTypes()[0];
1481: if (value == null
1482: || tClass.isAssignableFrom(value.getClass())) {
1483: m.invoke(source, new Object[] { value });
1484: log.debug("Set property '" + pd.getName() + "=" + value
1485: + "' on object '" + source.getClass().getName()
1486: + "'");
1487: } else if (DataTypeMapping.isMappable(value.getClass(),
1488: tClass)) {
1489: // See if there is a datatype mapper for these classes
1490: value = DataTypeMapping.map(value, tClass);
1491: m.invoke(source, new Object[] { value });
1492: log.debug("Translate+Set property '" + pd.getName()
1493: + "=" + value + "' on object '"
1494: + source.getClass().getName() + "'");
1495: } else {
1496: // Data type mismatch
1497: throw new MouldException(
1498: MouldException.DATATYPE_MISMATCH, source
1499: .getClass().getName()
1500: + "." + m.getName(), tClass.getName(),
1501: value.getClass().getName());
1502: }
1503: } else {
1504: MouldException me = new MouldException(
1505: MouldException.NO_SETTER, null, pd == null ? "???"
1506: : pd.getName(), source.getClass().getName());
1507: log.error(me.getLocalizedMessage());
1508: throw me;
1509: }
1510: }
1511:
1512: private static Object getProperty(PropertyDescriptor pd,
1513: Object source) throws IllegalAccessException,
1514: InvocationTargetException, MouldException {
1515: if (pd != null && pd.getReadMethod() != null) {
1516: Method m = pd.getReadMethod();
1517: if (!m.isAccessible())
1518: m.setAccessible(true);
1519: Object value = m.invoke(source, new Object[] {});
1520: log.debug("Get property '" + pd.getName() + "=" + value
1521: + "' on object '" + source.getClass().getName()
1522: + "'");
1523: return value;
1524: } else {
1525: MouldException me = new MouldException(
1526: MouldException.NO_GETTER, null, pd == null ? "???"
1527: : pd.getName(), source.getClass().getName());
1528: log.error(me.getLocalizedMessage());
1529: throw me;
1530: }
1531: }
1532:
1533: /** Set a value on a Bean, if its a persistent bean, try to use an update method first
1534: * (for v1.0 domain objects), otherwise use the setter (for v1.1 and above).
1535: */
1536: private static void updateProperty(PropertyDescriptor pd,
1537: Object value, Object source) throws IllegalAccessException,
1538: InvocationTargetException, MouldException {
1539: if (source instanceof Persistent) {
1540: if (pd != null && pd.getWriteMethod() != null) {
1541: try {
1542: Method m = source.getClass().getMethod(
1543: "update"
1544: + StringHelper.getUpper1(pd
1545: .getName()),
1546: pd.getWriteMethod().getParameterTypes());
1547: if (!m.isAccessible())
1548: m.setAccessible(true);
1549: Class tClass = m.getParameterTypes()[0];
1550: if (value == null
1551: || tClass
1552: .isAssignableFrom(value.getClass())) {
1553: m.invoke(source, new Object[] { value });
1554: log.debug("Update property '" + pd.getName()
1555: + "=" + value + "' on object '"
1556: + source.getClass().getName() + "'");
1557: // See if there is a datatype mapper for these classes
1558: } else if (DataTypeMapping.isMappable(value
1559: .getClass(), tClass)) {
1560: value = DataTypeMapping.map(value, tClass);
1561: m.invoke(source, new Object[] { value });
1562: log.debug("Translate+Update property '"
1563: + pd.getName() + "=" + value
1564: + "' on object '"
1565: + source.getClass().getName() + "'");
1566: } else {
1567: // Data type mismatch
1568: throw new MouldException(
1569: MouldException.DATATYPE_MISMATCH,
1570: source.getClass().getName() + "."
1571: + m.getName(),
1572: tClass.getName(), value.getClass()
1573: .getName());
1574: }
1575: } catch (NoSuchMethodException e) {
1576: log
1577: .debug("No Updator, try Setter for DO property '"
1578: + pd.getName()
1579: + "' on object '"
1580: + source.getClass().getName() + "'");
1581: // try to use the setter if there is no update method
1582: setProperty(pd, value, source);
1583: }
1584: } else {
1585: MouldException me = new MouldException(
1586: MouldException.NO_SETTER, null,
1587: pd == null ? "???" : pd.getName(), source
1588: .getClass().getName());
1589: log.error(me.getLocalizedMessage());
1590: throw me;
1591: }
1592: } else
1593: setProperty(pd, value, source);
1594: }
1595:
1596: }
|