0001: package org.apache.ojb.broker.core;
0002:
0003: /* Copyright 2003-2005 The Apache Software Foundation
0004: *
0005: * Licensed under the Apache License, Version 2.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: import java.lang.reflect.Array;
0019: import java.util.ArrayList;
0020: import java.util.Collection;
0021: import java.util.HashMap;
0022: import java.util.Iterator;
0023: import java.util.List;
0024: import java.util.Map;
0025:
0026: import org.apache.ojb.broker.Identity;
0027: import org.apache.ojb.broker.ManageableCollection;
0028: import org.apache.ojb.broker.PBLifeCycleEvent;
0029: import org.apache.ojb.broker.PersistenceBrokerException;
0030: import org.apache.ojb.broker.accesslayer.OJBIterator;
0031: import org.apache.ojb.broker.accesslayer.PagingIterator;
0032: import org.apache.ojb.broker.accesslayer.PlainPrefetcher;
0033: import org.apache.ojb.broker.accesslayer.RelationshipPrefetcher;
0034: import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
0035: import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
0036: import org.apache.ojb.broker.core.proxy.IndirectionHandler;
0037: import org.apache.ojb.broker.core.proxy.MaterializationListener;
0038: import org.apache.ojb.broker.core.proxy.ProxyHelper;
0039: import org.apache.ojb.broker.metadata.ClassDescriptor;
0040: import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
0041: import org.apache.ojb.broker.metadata.CollectionDescriptor;
0042: import org.apache.ojb.broker.metadata.FieldDescriptor;
0043: import org.apache.ojb.broker.metadata.FieldHelper;
0044: import org.apache.ojb.broker.metadata.MetadataException;
0045: import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
0046: import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
0047: import org.apache.ojb.broker.query.Criteria;
0048: import org.apache.ojb.broker.query.Query;
0049: import org.apache.ojb.broker.query.QueryByCriteria;
0050: import org.apache.ojb.broker.query.QueryFactory;
0051: import org.apache.ojb.broker.util.BrokerHelper;
0052: import org.apache.ojb.broker.util.collections.ManageableArrayList;
0053: import org.apache.ojb.broker.util.collections.ManageableHashSet;
0054: import org.apache.ojb.broker.util.collections.RemovalAwareCollection;
0055: import org.apache.ojb.broker.util.collections.RemovalAwareList;
0056: import org.apache.ojb.broker.util.collections.RemovalAwareSet;
0057: import org.apache.ojb.broker.util.logging.Logger;
0058: import org.apache.ojb.broker.util.logging.LoggerFactory;
0059:
0060: /**
0061: * Encapsulates 1:1 and 1:n references and collection references stuff.
0062: *
0063: * TODO: Should we made this class independend from PB implementation class
0064: * and only use PB interface methods?
0065: *
0066: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
0067: * @version $Id: QueryReferenceBroker.java,v 1.17.2.17 2005/12/21 22:25:00 tomdz Exp $
0068: */
0069: public class QueryReferenceBroker {
0070: private Logger log = LoggerFactory
0071: .getLogger(QueryReferenceBroker.class);
0072:
0073: private PersistenceBrokerImpl pb;
0074: private HashMap m_retrievalTasks;
0075: private ArrayList prefetchingListeners;
0076: private final boolean batchRetrieval = true;
0077: private final boolean prefetchProxies = true;
0078: private Class classToPrefetch;
0079: private PBLifeCycleEvent afterLookupEvent;
0080:
0081: public QueryReferenceBroker(final PersistenceBrokerImpl pb) {
0082: this .pb = pb;
0083: afterLookupEvent = new PBLifeCycleEvent(pb,
0084: PBLifeCycleEvent.Type.AFTER_LOOKUP);
0085: }
0086:
0087: /**
0088: * retrieve a collection of itemClass Objects matching the Query query
0089: * @param collectionClass type the collection to be returned
0090: * @param itemClass Class of item in collection
0091: * @param query the query
0092: */
0093: private ManageableCollection getCollectionByQuery(
0094: Class collectionClass, Class itemClass, Query query)
0095: throws ClassNotPersistenceCapableException,
0096: PersistenceBrokerException {
0097: if (log.isDebugEnabled())
0098: log.debug("getCollectionByQuery (" + collectionClass + ", "
0099: + itemClass + ", " + query + ")");
0100:
0101: ClassDescriptor cld = pb.getClassDescriptor(itemClass);
0102: ManageableCollection result = null;
0103: OJBIterator iter = null;
0104: int fullSize = -1;
0105: int size = 0;
0106:
0107: final boolean isRetrievalTasksCreated = batchRetrieval
0108: && m_retrievalTasks == null;
0109: if (isRetrievalTasksCreated) {
0110: // Maps ReferenceDescriptors to HashSets of owners
0111: m_retrievalTasks = new HashMap();
0112: }
0113:
0114: // ==> enable materialization cache
0115: pb.getInternalCache().enableMaterializationCache();
0116: try {
0117: result = (ManageableCollection) collectionClass
0118: .newInstance();
0119:
0120: // now iterate over all elements and add them to the new collection
0121: // lifecycle events are disabled
0122: iter = pb.getIteratorFromQuery(query, cld);
0123: iter.disableLifeCycleEvents();
0124:
0125: // BRJ : get fullSizefor Query
0126: // to be removed when Query.fullSize is removed
0127: if (iter instanceof PagingIterator) {
0128: fullSize = iter.fullSize();
0129: }
0130:
0131: while (iter.hasNext()) {
0132: Object candidate = iter.next();
0133:
0134: /**
0135: * MBAIRD
0136: * candidate CAN be null in the case of materializing from an iterator based
0137: * on a query for a class that is mapped to a table that has other classes
0138: * mapped to that table as well, but aren't extents.
0139: */
0140: if (candidate != null) {
0141: IndirectionHandler handler = ProxyHelper
0142: .getIndirectionHandler(candidate);
0143:
0144: if ((handler != null)
0145: || itemClass.isAssignableFrom(candidate
0146: .getClass())) {
0147: result.ojbAdd(candidate);
0148:
0149: // BRJ: count added objects
0150: // to be removed when Query.fullSize is removed
0151: size++;
0152: } else {
0153: //warn the user
0154: log
0155: .warn("Candidate object ["
0156: + candidate
0157: + "] class ["
0158: + candidate.getClass()
0159: .getName()
0160: + "] is not a subtype of ["
0161: + itemClass.getName()
0162: + "] or any type of proxy. NOT INCLUDED in result collection");
0163: }
0164: if (prefetchProxies && (handler != null)
0165: && (cld.getProxyPrefetchingLimit() > 0)
0166: && addRetrievalTask(candidate, this )) {
0167: new PBMaterializationListener(candidate,
0168: m_retrievalTasks, this , cld
0169: .getProxyPrefetchingLimit());
0170: }
0171: }
0172: }
0173:
0174: if (isRetrievalTasksCreated) {
0175: // turn off auto prefetching for related proxies
0176: final Class saveClassToPrefetch = classToPrefetch;
0177: classToPrefetch = null;
0178: try {
0179: performRetrievalTasks();
0180: } finally {
0181: classToPrefetch = saveClassToPrefetch;
0182: }
0183: }
0184:
0185: // BRJ: fire LifeCycleEvents after execution of RetrievalTasks
0186: // to ensure objects are fully materialized
0187: Iterator resultIter = result.ojbIterator();
0188: while (resultIter.hasNext()) {
0189: Object obj = resultIter.next();
0190: afterLookupEvent.setTarget(obj);
0191: pb.fireBrokerEvent(afterLookupEvent);
0192: afterLookupEvent.setTarget(null);
0193: }
0194:
0195: // ==> disable materialization cache
0196: pb.getInternalCache().disableMaterializationCache();
0197: } catch (RuntimeException e) {
0198: // ==> clear materialization cache
0199: pb.getInternalCache().doLocalClear();
0200: throw e;
0201: } catch (Exception ex) {
0202: // ==> clear materialization cache
0203: pb.getInternalCache().doLocalClear();
0204: log.error(ex);
0205: throw new PersistenceBrokerException(ex);
0206: } finally {
0207: if (iter != null) {
0208: iter.releaseDbResources();
0209: }
0210: if (isRetrievalTasksCreated) {
0211: m_retrievalTasks = null;
0212: }
0213: }
0214:
0215: // BRJ: store fullSize in Query to re-enable deprecated functionality
0216: // to be removed when Query.fullSize is removed
0217: if (fullSize < 0) {
0218: fullSize = size; // use size of result
0219: }
0220: query.fullSize(fullSize);
0221:
0222: return result;
0223: }
0224:
0225: /**
0226: * retrieve a collection of type collectionClass matching the Query query
0227: * if lazy = true return a CollectionProxy
0228: *
0229: * @param collectionClass
0230: * @param query
0231: * @param lazy
0232: * @return ManageableCollection
0233: * @throws PersistenceBrokerException
0234: */
0235: public ManageableCollection getCollectionByQuery(
0236: Class collectionClass, Query query, boolean lazy)
0237: throws PersistenceBrokerException {
0238: ManageableCollection result;
0239:
0240: try {
0241: // BRJ: return empty Collection for null query
0242: if (query == null) {
0243: result = (ManageableCollection) collectionClass
0244: .newInstance();
0245: } else {
0246: if (lazy) {
0247: result = pb.getProxyFactory()
0248: .createCollectionProxy(pb.getPBKey(),
0249: query, collectionClass);
0250: } else {
0251: result = getCollectionByQuery(collectionClass,
0252: query.getSearchClass(), query);
0253: }
0254: }
0255: return result;
0256: } catch (Exception e) {
0257: if (e instanceof PersistenceBrokerException) {
0258: throw (PersistenceBrokerException) e;
0259: } else {
0260: throw new PersistenceBrokerException(e);
0261: }
0262: }
0263: }
0264:
0265: /**
0266: * retrieve a collection of itemClass Objects matching the Query query
0267: */
0268: public Collection getCollectionByQuery(Query query, boolean lazy)
0269: throws PersistenceBrokerException {
0270: // thma: the following cast is safe because:
0271: // 1. ManageableVector implements Collection (will be returned if lazy == false)
0272: // 2. CollectionProxy implements Collection (will be returned if lazy == true)
0273: return (Collection) getCollectionByQuery(
0274: RemovalAwareCollection.class, query, lazy);
0275: }
0276:
0277: private Class getCollectionTypeClass(CollectionDescriptor cds)
0278: throws PersistenceBrokerException {
0279: // BRJ: do not use RemovalAwareCollection for m:n relationships
0280: // see http://db.apache.org/ojb/docu/guides/basic-technique.html#Mapping+m%3An+associations
0281:
0282: Class fieldType = cds.getPersistentField().getType();
0283: Class collType;
0284:
0285: if (fieldType.isArray()
0286: || fieldType
0287: .isAssignableFrom(RemovalAwareCollection.class)) {
0288: collType = cds.isMtoNRelation() ? ManageableArrayList.class
0289: : RemovalAwareCollection.class;
0290: } else if (fieldType.isAssignableFrom(RemovalAwareList.class)) {
0291: collType = cds.isMtoNRelation() ? ManageableArrayList.class
0292: : RemovalAwareList.class;
0293: } else if (fieldType.isAssignableFrom(RemovalAwareSet.class)) {
0294: collType = cds.isMtoNRelation() ? ManageableHashSet.class
0295: : RemovalAwareSet.class;
0296: } else if (ManageableCollection.class
0297: .isAssignableFrom(fieldType)) {
0298: collType = fieldType;
0299: } else {
0300: throw new MetadataException(
0301: "Cannot determine a default collection type for collection "
0302: + cds.getAttributeName()
0303: + " in type "
0304: + cds.getClassDescriptor()
0305: .getClassNameOfObject());
0306: }
0307: return collType;
0308: }
0309:
0310: /**
0311: * @return true if this is the first task for the given ObjectReferenceDescriptor
0312: */
0313: private boolean addRetrievalTask(Object obj, Object key) {
0314: ArrayList owners = (ArrayList) m_retrievalTasks.get(key);
0315: boolean isFirst = false;
0316:
0317: if (owners == null) {
0318: owners = new ArrayList();
0319: m_retrievalTasks.put(key, owners);
0320: isFirst = true;
0321: }
0322: owners.add(obj);
0323: return isFirst;
0324: }
0325:
0326: /**
0327: * Perform the stored retrieval tasks
0328: * BRJ: made it public to access it from BasePrefetcher
0329: * TODO: this is a quick fix !
0330: */
0331: public void performRetrievalTasks() {
0332: if (m_retrievalTasks == null) {
0333: return;
0334: }
0335:
0336: while (m_retrievalTasks.size() > 0) {
0337: HashMap tmp = m_retrievalTasks;
0338: m_retrievalTasks = new HashMap();
0339: // during execution of these tasks new tasks may be added
0340:
0341: for (Iterator it = tmp.entrySet().iterator(); it.hasNext();) {
0342: Map.Entry entry = (Map.Entry) it.next();
0343: Object key = entry.getKey();
0344:
0345: if (!(key instanceof ObjectReferenceDescriptor)) {
0346: continue;
0347: }
0348:
0349: ObjectReferenceDescriptor ord = (ObjectReferenceDescriptor) key;
0350: RelationshipPrefetcher prefetcher;
0351: ArrayList owners = (ArrayList) entry.getValue();
0352:
0353: // if (ord instanceof SuperReferenceDescriptor || ord.isLazy() || (ord.getItemProxyClass() != null))
0354: if (ord.isLazy() || (ord.getItemProxyClass() != null)) {
0355: continue;
0356: }
0357:
0358: prefetcher = pb.getRelationshipPrefetcherFactory()
0359: .createRelationshipPrefetcher(ord);
0360: prefetcher.prefetchRelationship(owners);
0361: it.remove();
0362: }
0363: }
0364: }
0365:
0366: /**
0367: * Retrieve a single Reference.
0368: * This implementation retrieves a referenced object from the data backend
0369: * if <b>cascade-retrieve</b> is true or if <b>forced</b> is true.
0370: *
0371: * @param obj - object that will have it's field set with a referenced object.
0372: * @param cld - the ClassDescriptor describring obj
0373: * @param rds - the ObjectReferenceDescriptor of the reference attribute to be loaded
0374: * @param forced - if set to true, the reference is loaded even if the rds differs.
0375: */
0376: public void retrieveReference(Object obj, ClassDescriptor cld,
0377: ObjectReferenceDescriptor rds, boolean forced) {
0378: PersistentField refField;
0379: Object refObj = null;
0380:
0381: if (forced || rds.getCascadeRetrieve()) {
0382: pb.getInternalCache().enableMaterializationCache();
0383: try {
0384: Identity id = getReferencedObjectIdentity(obj, rds, cld);
0385: boolean isRefObjDefined = true;
0386:
0387: if (id == null) {
0388: refObj = null;
0389: } //JMM : why not see if the object has already been loaded
0390: else if (pb.serviceObjectCache().lookup(id) != null) {
0391: refObj = pb.doGetObjectByIdentity(id);
0392: if (rds.isSuperReferenceDescriptor()) {
0393: // walk the super-references
0394: ClassDescriptor super Cld = cld.getRepository()
0395: .getDescriptorFor(rds.getItemClass());
0396: retrieveReferences(refObj, super Cld, false);
0397: retrieveCollections(refObj, super Cld, false);
0398: }
0399: } else if ((m_retrievalTasks != null) && !rds.isLazy()
0400: && (rds.getItemProxyClass() == null)) {
0401: addRetrievalTask(obj, rds);
0402: isRefObjDefined = false;
0403: } else {
0404: refObj = getReferencedObject(id, rds);
0405: }
0406:
0407: if (isRefObjDefined) {
0408: refField = rds.getPersistentField();
0409: refField.set(obj, refObj);
0410:
0411: if ((refObj != null) && prefetchProxies
0412: && (m_retrievalTasks != null)
0413: && (rds.getProxyPrefetchingLimit() > 0)) {
0414: IndirectionHandler handler = ProxyHelper
0415: .getIndirectionHandler(refObj);
0416:
0417: if ((handler != null)
0418: && addRetrievalTask(obj, rds)) {
0419: new PBMaterializationListener(obj,
0420: m_retrievalTasks, rds, rds
0421: .getProxyPrefetchingLimit());
0422: }
0423: }
0424: }
0425:
0426: pb.getInternalCache().disableMaterializationCache();
0427: } catch (RuntimeException e) {
0428: pb.getInternalCache().doLocalClear();
0429: throw e;
0430: }
0431: }
0432: }
0433:
0434: /**
0435: * Retrieve a single Reference.
0436: * This implementation retrieves a referenced object from the data backend
0437: * if <b>cascade-retrieve</b> is true or if <b>forced</b> is true.
0438: *
0439: * @param obj - object that will have it's field set with a referenced object.
0440: * @param cld - the ClassDescriptor describring obj
0441: * @param rds - the ObjectReferenceDescriptor of the reference attribute to be loaded
0442: * @param forced - if set to true, the reference is loaded even if the rds differs.
0443: */
0444: public void retrieveProxyReference(Object obj, ClassDescriptor cld,
0445: ObjectReferenceDescriptor rds, boolean forced) {
0446: PersistentField refField;
0447: Object refObj = null;
0448:
0449: pb.getInternalCache().enableMaterializationCache();
0450: try {
0451: Identity id = getReferencedObjectIdentity(obj, rds, cld);
0452: if (id != null) {
0453: refObj = pb.createProxy(rds.getItemClass(), id);
0454: }
0455: refField = rds.getPersistentField();
0456: refField.set(obj, refObj);
0457:
0458: if ((refObj != null) && prefetchProxies
0459: && (m_retrievalTasks != null)
0460: && (rds.getProxyPrefetchingLimit() > 0)) {
0461: IndirectionHandler handler = ProxyHelper
0462: .getIndirectionHandler(refObj);
0463:
0464: if ((handler != null) && addRetrievalTask(obj, rds)) {
0465: new PBMaterializationListener(obj,
0466: m_retrievalTasks, rds, rds
0467: .getProxyPrefetchingLimit());
0468: }
0469: }
0470:
0471: pb.getInternalCache().disableMaterializationCache();
0472: } catch (RuntimeException e) {
0473: pb.getInternalCache().doLocalClear();
0474: throw e;
0475: }
0476:
0477: }
0478:
0479: /**
0480: * Retrieve all References
0481: *
0482: * @param newObj the instance to be loaded or refreshed
0483: * @param cld the ClassDescriptor of the instance
0484: * @param forced if set to true loading is forced even if cld differs.
0485: */
0486: public void retrieveReferences(Object newObj, ClassDescriptor cld,
0487: boolean forced) throws PersistenceBrokerException {
0488: Iterator i = cld.getObjectReferenceDescriptors().iterator();
0489:
0490: // turn off auto prefetching for related proxies
0491: final Class saveClassToPrefetch = classToPrefetch;
0492: classToPrefetch = null;
0493:
0494: pb.getInternalCache().enableMaterializationCache();
0495: try {
0496: while (i.hasNext()) {
0497: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i
0498: .next();
0499: retrieveReference(newObj, cld, rds, forced);
0500: }
0501:
0502: pb.getInternalCache().disableMaterializationCache();
0503: } catch (RuntimeException e) {
0504: pb.getInternalCache().doLocalClear();
0505: throw e;
0506: } finally {
0507: classToPrefetch = saveClassToPrefetch;
0508: }
0509: }
0510:
0511: /**
0512: * Retrieve all References
0513: *
0514: * @param newObj the instance to be loaded or refreshed
0515: * @param cld the ClassDescriptor of the instance
0516: * @param forced if set to true loading is forced even if cld differs.
0517: */
0518: public void retrieveProxyReferences(Object newObj,
0519: ClassDescriptor cld, boolean forced)
0520: throws PersistenceBrokerException {
0521: Iterator i = cld.getObjectReferenceDescriptors().iterator();
0522:
0523: // turn off auto prefetching for related proxies
0524: final Class saveClassToPrefetch = classToPrefetch;
0525: classToPrefetch = null;
0526:
0527: pb.getInternalCache().enableMaterializationCache();
0528: try {
0529: while (i.hasNext()) {
0530: ObjectReferenceDescriptor rds = (ObjectReferenceDescriptor) i
0531: .next();
0532: retrieveProxyReference(newObj, cld, rds, forced);
0533: }
0534:
0535: pb.getInternalCache().disableMaterializationCache();
0536: } catch (RuntimeException e) {
0537: pb.getInternalCache().doLocalClear();
0538: throw e;
0539: } finally {
0540: classToPrefetch = saveClassToPrefetch;
0541: }
0542: }
0543:
0544: /**
0545: * retrieves an Object reference's Identity.
0546: * <br>
0547: * Null is returned if all foreign keys are null
0548: */
0549: private Identity getReferencedObjectIdentity(Object obj,
0550: ObjectReferenceDescriptor rds, ClassDescriptor cld) {
0551: Object[] fkValues = rds.getForeignKeyValues(obj, cld);
0552: FieldDescriptor[] fkFieldDescriptors = rds
0553: .getForeignKeyFieldDescriptors(cld);
0554: boolean hasNullifiedFKValue = hasNullifiedFK(
0555: fkFieldDescriptors, fkValues);
0556: /*
0557: BRJ: if all fk values are null there's no referenced object
0558:
0559: arminw: Supposed the given object has nullified FK values but the referenced
0560: object still exists. This could happend after serialization of the main object. In
0561: this case all anonymous field (AK) information is lost, because AnonymousPersistentField class
0562: use the object identity to cache the AK values. But we can build Identity anyway from the reference
0563: */
0564: if (hasNullifiedFKValue) {
0565: if (BrokerHelper.hasAnonymousKeyReference(cld, rds)) {
0566: Object referencedObject = rds.getPersistentField().get(
0567: obj);
0568: if (referencedObject != null) {
0569: return pb.serviceIdentity().buildIdentity(
0570: referencedObject);
0571: }
0572: }
0573: } else {
0574: // ensure that top-level extents are used for Identities
0575: return pb.serviceIdentity().buildIdentity(
0576: rds.getItemClass(),
0577: pb.getTopLevelClass(rds.getItemClass()), fkValues);
0578: }
0579: return null;
0580: }
0581:
0582: // BRJ: check if we have non null fk values
0583: // TBD we should also check primitives
0584: // to avoid creation of unmaterializable proxies
0585: private boolean hasNullifiedFK(
0586: FieldDescriptor[] fkFieldDescriptors, Object[] fkValues) {
0587: boolean result = true;
0588: for (int i = 0; i < fkValues.length; i++) {
0589: if (!pb.serviceBrokerHelper().representsNull(
0590: fkFieldDescriptors[i], fkValues[i])) {
0591: result = false;
0592: break;
0593: }
0594: }
0595: return result;
0596: }
0597:
0598: /**
0599: * retrieves an Object reference by its Identity.
0600: * <br>
0601: * If there is a Proxy-class is defined in the ReferenceDescriptor or
0602: * if the ReferenceDescriptor is lazy, a Proxy-object is returned.
0603: * <br>
0604: * If no Proxy-class is defined, a getObjectByIdentity(...) lookup is performed.
0605: */
0606: private Object getReferencedObject(Identity id,
0607: ObjectReferenceDescriptor rds) {
0608: Class baseClassForProxy;
0609:
0610: if (rds.isLazy()) {
0611: /*
0612: arminw:
0613: use real reference class instead of the top-level class,
0614: because we want to use a proxy representing the real class
0615: not only the top-level class - right?
0616: */
0617: // referencedProxy = getClassDescriptor(referencedClass).getDynamicProxyClass();
0618: //referencedProxy = rds.getItemClass();
0619: /*
0620: * andrew.clute:
0621: * With proxy generation now handled by the ProxyFactory implementations, the class of the Item
0622: * is now the nessecary parameter to generate a proxy.
0623: */
0624: baseClassForProxy = rds.getItemClass();
0625: } else {
0626: /*
0627: * andrew.clute:
0628: * If the descriptor does not mark it as lazy, then the class for the proxy must be of type VirtualProxy
0629: */
0630: baseClassForProxy = rds.getItemProxyClass();
0631: }
0632:
0633: if (baseClassForProxy != null) {
0634: try {
0635: return pb.createProxy(baseClassForProxy, id);
0636: } catch (Exception e) {
0637: log.error("Error while instantiate object " + id
0638: + ", msg: " + e.getMessage(), e);
0639: if (e instanceof PersistenceBrokerException) {
0640: throw (PersistenceBrokerException) e;
0641: } else {
0642: throw new PersistenceBrokerException(e);
0643: }
0644: }
0645: } else {
0646: return pb.doGetObjectByIdentity(id);
0647: }
0648: }
0649:
0650: /**
0651: * Retrieve a single Collection on behalf of <b>obj</b>.
0652: * The Collection is retrieved only if <b>cascade.retrieve is true</b>
0653: * or if <b>forced</b> is set to true. *
0654: *
0655: * @param obj - the object to be updated
0656: * @param cld - the ClassDescriptor describing obj
0657: * @param cds - the CollectionDescriptor describing the collection attribute to be loaded
0658: * @param forced - if set to true loading is forced, even if cds differs.
0659: *
0660: */
0661: public void retrieveCollection(Object obj, ClassDescriptor cld,
0662: CollectionDescriptor cds, boolean forced) {
0663: doRetrieveCollection(obj, cld, cds, forced, cds.isLazy());
0664: }
0665:
0666: /**
0667: * Retrieve a single Proxied Collection on behalf of <b>obj</b>.
0668: * The Collection is retrieved only if <b>cascade.retrieve is true</b>
0669: * or if <b>forced</b> is set to true. *
0670: *
0671: * @param obj - the object to be updated
0672: * @param cld - the ClassDescriptor describing obj
0673: * @param cds - the CollectionDescriptor describing the collection attribute to be loaded
0674: * @param forced - if set to true a proxy will be placed, even if cds differs.
0675: *
0676: */
0677: public void retrieveProxyCollection(Object obj,
0678: ClassDescriptor cld, CollectionDescriptor cds,
0679: boolean forced) {
0680: doRetrieveCollection(obj, cld, cds, forced, true);
0681: }
0682:
0683: private void doRetrieveCollection(Object obj, ClassDescriptor cld,
0684: CollectionDescriptor cds, boolean forced, boolean lazyLoad) {
0685: if (forced || cds.getCascadeRetrieve()) {
0686: if ((m_retrievalTasks != null) && !cds.isLazy()
0687: && !cds.hasProxyItems()
0688: && (cds.getQueryCustomizer() == null)) {
0689: addRetrievalTask(obj, cds);
0690: } else {
0691: // this collection type will be used:
0692: Class collectionClass = cds.getCollectionClass();
0693: PersistentField collectionField = cds
0694: .getPersistentField();
0695: Query fkQuery = getFKQuery(obj, cld, cds);
0696: Object value;
0697:
0698: pb.getInternalCache().enableMaterializationCache();
0699: try {
0700: if (collectionClass == null) {
0701: Collection result = (Collection) getCollectionByQuery(
0702: getCollectionTypeClass(cds), fkQuery,
0703: lazyLoad);
0704:
0705: // assign collection to objects attribute
0706: // if attribute has an array type build an array, else assign collection directly
0707: if (collectionField.getType().isArray()) {
0708: int length = result.size();
0709: Class itemtype = collectionField.getType()
0710: .getComponentType();
0711: Object resultArray = Array.newInstance(
0712: itemtype, length);
0713: int j = 0;
0714: for (Iterator iter = result.iterator(); iter
0715: .hasNext(); j++) {
0716: Array.set(resultArray, j, iter.next());
0717: }
0718: collectionField.set(obj, resultArray);
0719: } else {
0720: collectionField.set(obj, result);
0721: }
0722: value = result;
0723: } else {
0724: ManageableCollection result = getCollectionByQuery(
0725: collectionClass, fkQuery, lazyLoad);
0726: collectionField.set(obj, result);
0727: value = result;
0728: }
0729:
0730: if (prefetchProxies && (m_retrievalTasks != null)
0731: && (cds.getProxyPrefetchingLimit() > 0)
0732: && (cds.getQueryCustomizer() == null)
0733: && (ProxyHelper.isCollectionProxy(value))) {
0734: if (addRetrievalTask(obj, cds)) {
0735: new PBCollectionProxyListener(obj,
0736: m_retrievalTasks, cds, cds
0737: .getProxyPrefetchingLimit());
0738: }
0739: }
0740:
0741: pb.getInternalCache().disableMaterializationCache();
0742: } catch (RuntimeException e) {
0743: pb.getInternalCache().doLocalClear();
0744: throw e;
0745: }
0746: }
0747: }
0748: }
0749:
0750: /**
0751: * Answer the foreign key query to retrieve the collection
0752: * defined by CollectionDescriptor
0753: */
0754: private Query getFKQuery(Object obj, ClassDescriptor cld,
0755: CollectionDescriptor cds) {
0756: Query fkQuery;
0757: QueryByCriteria fkQueryCrit;
0758:
0759: if (cds.isMtoNRelation()) {
0760: fkQueryCrit = getFKQueryMtoN(obj, cld, cds);
0761: } else {
0762: fkQueryCrit = getFKQuery1toN(obj, cld, cds);
0763: }
0764:
0765: // check if collection must be ordered
0766: if (!cds.getOrderBy().isEmpty()) {
0767: Iterator iter = cds.getOrderBy().iterator();
0768: while (iter.hasNext()) {
0769: fkQueryCrit.addOrderBy((FieldHelper) iter.next());
0770: }
0771: }
0772:
0773: // BRJ: customize the query
0774: if (cds.getQueryCustomizer() != null) {
0775: fkQuery = cds.getQueryCustomizer().customizeQuery(obj, pb,
0776: cds, fkQueryCrit);
0777: } else {
0778: fkQuery = fkQueryCrit;
0779: }
0780:
0781: return fkQuery;
0782: }
0783:
0784: /**
0785: * Get Foreign key query for m:n <br>
0786: * supports UNIDIRECTIONAL m:n using QueryByMtoNCriteria
0787: * @return org.apache.ojb.broker.query.QueryByCriteria
0788: * @param obj the owner of the relationship
0789: * @param cld the ClassDescriptor for the owner
0790: * @param cod the CollectionDescriptor
0791: */
0792: private QueryByCriteria getFKQueryMtoN(Object obj,
0793: ClassDescriptor cld, CollectionDescriptor cod) {
0794: ValueContainer[] values = pb.serviceBrokerHelper()
0795: .getKeyValues(cld, obj);
0796: Object[] this ClassFks = cod.getFksToThisClass();
0797: Object[] itemClassFks = cod.getFksToItemClass();
0798: ClassDescriptor refCld = pb.getClassDescriptor(cod
0799: .getItemClass());
0800: Criteria criteria = new Criteria();
0801:
0802: for (int i = 0; i < this ClassFks.length; i++) {
0803: criteria.addEqualTo(cod.getIndirectionTable() + "."
0804: + this ClassFks[i], values[i].getValue());
0805: }
0806: for (int i = 0; i < itemClassFks.length; i++) {
0807: criteria.addEqualToField(cod.getIndirectionTable() + "."
0808: + itemClassFks[i], refCld.getPkFields()[i]
0809: .getAttributeName());
0810: }
0811:
0812: return QueryFactory.newQuery(refCld.getClassOfObject(), cod
0813: .getIndirectionTable(), criteria);
0814: }
0815:
0816: /**
0817: * Get Foreign key query for 1:n
0818: * @return org.apache.ojb.broker.query.QueryByCriteria
0819: * @param obj
0820: * @param cld
0821: * @param cod
0822: */
0823: private QueryByCriteria getFKQuery1toN(Object obj,
0824: ClassDescriptor cld, CollectionDescriptor cod) {
0825: ValueContainer[] container = pb.serviceBrokerHelper()
0826: .getKeyValues(cld, obj);
0827: ClassDescriptor refCld = pb.getClassDescriptor(cod
0828: .getItemClass());
0829: FieldDescriptor[] fields = cod
0830: .getForeignKeyFieldDescriptors(refCld);
0831: Criteria criteria = new Criteria();
0832:
0833: for (int i = 0; i < fields.length; i++) {
0834: FieldDescriptor fld = fields[i];
0835: criteria.addEqualTo(fld.getAttributeName(), container[i]
0836: .getValue());
0837: }
0838:
0839: return QueryFactory.newQuery(refCld.getClassOfObject(),
0840: criteria);
0841: }
0842:
0843: /**
0844: * Answer the primary key query to retrieve an Object
0845: *
0846: * @param oid the Identity of the Object to retrieve
0847: * @return The resulting query
0848: */
0849: public Query getPKQuery(Identity oid) {
0850: Object[] values = oid.getPrimaryKeyValues();
0851: ClassDescriptor cld = pb.getClassDescriptor(oid
0852: .getObjectsTopLevelClass());
0853: FieldDescriptor[] fields = cld.getPkFields();
0854: Criteria criteria = new Criteria();
0855:
0856: for (int i = 0; i < fields.length; i++) {
0857: FieldDescriptor fld = fields[i];
0858: criteria.addEqualTo(fld.getAttributeName(), values[i]);
0859: }
0860: return QueryFactory.newQuery(cld.getClassOfObject(), criteria);
0861: }
0862:
0863: /**
0864: * Retrieve all Collection attributes of a given instance
0865: *
0866: * @param newObj the instance to be loaded or refreshed
0867: * @param cld the ClassDescriptor of the instance
0868: * @param forced if set to true, loading is forced even if cld differs
0869: *
0870: */
0871: public void retrieveCollections(Object newObj, ClassDescriptor cld,
0872: boolean forced) throws PersistenceBrokerException {
0873: doRetrieveCollections(newObj, cld, forced, false);
0874: }
0875:
0876: /**
0877: * Retrieve all Collection attributes of a given instance, and make all of the Proxy Collections
0878: *
0879: * @param newObj the instance to be loaded or refreshed
0880: * @param cld the ClassDescriptor of the instance
0881: * @param forced if set to true, loading is forced even if cld differs
0882: *
0883: */
0884: public void retrieveProxyCollections(Object newObj,
0885: ClassDescriptor cld, boolean forced)
0886: throws PersistenceBrokerException {
0887: doRetrieveCollections(newObj, cld, forced, true);
0888: }
0889:
0890: private void doRetrieveCollections(Object newObj,
0891: ClassDescriptor cld, boolean forced,
0892: boolean forceProxyCollection)
0893: throws PersistenceBrokerException {
0894: Iterator i = cld.getCollectionDescriptors().iterator();
0895:
0896: // turn off auto prefetching for related proxies
0897: final Class saveClassToPrefetch = classToPrefetch;
0898: classToPrefetch = null;
0899:
0900: pb.getInternalCache().enableMaterializationCache();
0901: try {
0902: while (i.hasNext()) {
0903: CollectionDescriptor cds = (CollectionDescriptor) i
0904: .next();
0905: if (forceProxyCollection) {
0906: retrieveProxyCollection(newObj, cld, cds, forced);
0907: } else {
0908: retrieveCollection(newObj, cld, cds, forced);
0909: }
0910: }
0911: pb.getInternalCache().disableMaterializationCache();
0912: } catch (RuntimeException e) {
0913: pb.getInternalCache().doLocalClear();
0914: throw e;
0915: } finally {
0916: classToPrefetch = saveClassToPrefetch;
0917: }
0918: }
0919:
0920: /**
0921: * remove all prefetching listeners
0922: */
0923: public void removePrefetchingListeners() {
0924: if (prefetchingListeners != null) {
0925: for (Iterator it = prefetchingListeners.iterator(); it
0926: .hasNext();) {
0927: PBPrefetchingListener listener = (PBPrefetchingListener) it
0928: .next();
0929: listener.removeThisListener();
0930: }
0931: prefetchingListeners.clear();
0932: }
0933: }
0934:
0935: public Class getClassToPrefetch() {
0936: return classToPrefetch;
0937: }
0938:
0939: //**********************************************************************
0940: // inner classes
0941: //**********************************************************************
0942:
0943: class PBMaterializationListener extends PBPrefetchingListener
0944: implements MaterializationListener {
0945: private IndirectionHandler _listenedHandler;
0946:
0947: PBMaterializationListener(Object owner, HashMap retrievalTasks,
0948: Object key, int limit) {
0949: super (owner, retrievalTasks, key, limit);
0950: }
0951:
0952: protected void addThisListenerTo(Object owner) {
0953: _listenedHandler = ProxyHelper.getIndirectionHandler(owner);
0954:
0955: if (_listenedHandler != null) {
0956: _listenedHandler.addListener(this );
0957: }
0958: }
0959:
0960: protected void removeThisListener() {
0961: if (_listenedHandler != null) {
0962: _listenedHandler.removeListener(this );
0963: _listenedHandler = null;
0964: }
0965: }
0966:
0967: protected RelationshipPrefetcher getPrefetcher(
0968: Object listenedObject) {
0969: if (_key instanceof ObjectReferenceDescriptor) {
0970: return pb.getRelationshipPrefetcherFactory()
0971: .createRelationshipPrefetcher(
0972: (ObjectReferenceDescriptor) _key);
0973: } else // PersistentBrokerImpl.this
0974: {
0975: // a special case: current collection being loaded contains proxies,
0976: // just load them without setting any fields
0977: IndirectionHandler handler = (IndirectionHandler) listenedObject;
0978: return new PlainPrefetcher(pb, handler.getIdentity()
0979: .getObjectsTopLevelClass());
0980: }
0981: }
0982:
0983: public void beforeMaterialization(IndirectionHandler handler,
0984: Identity oid) {
0985: prefetch(handler);
0986: }
0987:
0988: public void afterMaterialization(IndirectionHandler handler,
0989: Object materializedObject) {
0990: //do nothing
0991: }
0992: }
0993:
0994: abstract class PBPrefetchingListener {
0995: private HashMap _retrievalTasks;
0996: private int _limit;
0997: protected Object _key;
0998:
0999: PBPrefetchingListener(Object owner, HashMap retrievalTasks,
1000: Object key, int limit) {
1001: _retrievalTasks = retrievalTasks;
1002: _key = key;
1003: _limit = limit + 1; // lestenedObject + next limit objects
1004: if (prefetchingListeners == null) {
1005: prefetchingListeners = new ArrayList();
1006: }
1007: addThisListenerTo(owner);
1008: prefetchingListeners.add(this );
1009: }
1010:
1011: abstract protected void addThisListenerTo(Object owner);
1012:
1013: abstract protected void removeThisListener();
1014:
1015: abstract protected RelationshipPrefetcher getPrefetcher(
1016: Object listenedObject);
1017:
1018: protected void prefetch(Object listenedObject) {
1019: ArrayList owners = (ArrayList) _retrievalTasks.get(_key);
1020: List toPrefetch;
1021: RelationshipPrefetcher prefetcher;
1022: boolean prefetchingAll;
1023:
1024: removeThisListener();
1025:
1026: if (owners == null) {
1027: return;
1028: }
1029:
1030: prefetcher = getPrefetcher(listenedObject);
1031:
1032: if (owners.size() <= _limit) {
1033: toPrefetch = owners;
1034: prefetchingAll = true;
1035: } else {
1036: toPrefetch = owners.subList(0, _limit);
1037: prefetchingAll = false;
1038: }
1039:
1040: final Class saveClassToPrefetch = classToPrefetch;
1041: classToPrefetch = prefetcher.getItemClassDescriptor()
1042: .getClassOfObject();
1043: try {
1044: prefetcher.prefetchRelationship(toPrefetch);
1045: } finally {
1046: classToPrefetch = saveClassToPrefetch;
1047: }
1048:
1049: if (prefetchingAll) {
1050: _retrievalTasks.remove(_key);
1051: } else {
1052: // ArrayList documented trick:
1053: // "the following idiom removes a range of elements from a list:
1054: // list.subList(from, to).clear();
1055: toPrefetch.clear();
1056: addThisListenerTo(owners.get(0));
1057: }
1058: }
1059: }
1060:
1061: class PBCollectionProxyListener extends PBPrefetchingListener
1062: implements CollectionProxyListener {
1063: CollectionProxyDefaultImpl _listenedCollection;
1064:
1065: PBCollectionProxyListener(Object owner, HashMap retrievalTasks,
1066: CollectionDescriptor key, int limit) {
1067: super (owner, retrievalTasks, key, limit);
1068: }
1069:
1070: protected void addThisListenerTo(Object owner) {
1071: PersistentField collectionField = ((CollectionDescriptor) _key)
1072: .getPersistentField();
1073: _listenedCollection = (CollectionProxyDefaultImpl) collectionField
1074: .get(owner);
1075: _listenedCollection.addListener(this );
1076: }
1077:
1078: protected void removeThisListener() {
1079: if (_listenedCollection != null) {
1080: _listenedCollection.removeListener(this );
1081: _listenedCollection = null;
1082: }
1083: }
1084:
1085: protected RelationshipPrefetcher getPrefetcher(
1086: Object listenedObject) {
1087: return pb.getRelationshipPrefetcherFactory()
1088: .createRelationshipPrefetcher(
1089: (CollectionDescriptor) _key);
1090: }
1091:
1092: public void beforeLoading(CollectionProxyDefaultImpl col) {
1093: prefetch(col);
1094: }
1095:
1096: public void afterLoading(CollectionProxyDefaultImpl col) {
1097: //do nothing
1098: }
1099: }
1100: }
|