001: package org.apache.ojb.otm.core;
002:
003: /* Copyright 2003-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import org.apache.ojb.broker.*;
019: import org.apache.ojb.broker.cache.ObjectCache;
020: import org.apache.ojb.broker.core.proxy.ProxyHelper;
021: import org.apache.ojb.broker.accesslayer.OJBIterator;
022: import org.apache.ojb.broker.metadata.ClassDescriptor;
023: import org.apache.ojb.broker.query.Query;
024: import org.apache.ojb.broker.query.ReportQuery;
025: import org.apache.ojb.broker.util.configuration.ConfigurationException;
026: import org.apache.ojb.broker.util.configuration.Configurator;
027: import org.apache.ojb.odmg.oql.EnhancedOQLQuery;
028: import org.apache.ojb.odmg.oql.OQLQueryImpl;
029: import org.apache.ojb.otm.EditingContext;
030: import org.apache.ojb.otm.OTMConnection;
031: import org.apache.ojb.otm.copy.ObjectCopyStrategy;
032: import org.apache.ojb.otm.lock.LockType;
033: import org.apache.ojb.otm.lock.LockingException;
034: import org.odmg.ODMGRuntimeException;
035: import org.odmg.OQLQuery;
036:
037: import java.util.Collection;
038: import java.util.Iterator;
039: import java.util.ListIterator;
040: import java.util.ArrayList;
041:
042: /**
043: *
044: * <javadoc>
045: *
046: * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird </a>
047: * @author <a href="mailto:rraghuram@hotmail.com">Raghu Rajah </a>
048: * @version $Id: BaseConnection.java,v 1.37.2.2 2005/12/21 22:30:18 tomdz Exp $
049: *
050: */
051: public abstract class BaseConnection implements OTMConnection {
052:
053: private PersistenceBroker _pb;
054: private Transaction _tx;
055: private ConcreteEditingContext _editingContext;
056: private Configurator m_configurator;
057:
058: /**
059: * Constructor for BaseConnection.
060: *
061: */
062: public BaseConnection(PBKey pbKey) {
063: _pb = PersistenceBrokerFactory.createPersistenceBroker(pbKey);
064: m_configurator = PersistenceBrokerFactory.getConfigurator();
065: }
066:
067: public void close() {
068: _pb.close();
069: _pb = null;
070: }
071:
072: public boolean isClosed() {
073: if (_pb == null)
074: return true;
075: else
076: return _pb.isClosed();
077: }
078:
079: public PersistenceBroker getKernelBroker() {
080: return _pb;
081: }
082:
083: public void setTransaction(Transaction transaction) {
084: if (transaction == null) {
085: _editingContext = null;
086: } else if (_tx != null) {
087: throw new IllegalStateException(
088: "OTMConnection is already bound to the transacaction "
089: + _tx);
090: } else {
091: _editingContext = new ConcreteEditingContext(transaction,
092: _pb);
093: }
094: _tx = transaction;
095: }
096:
097: public Transaction getTransaction() {
098: return _tx;
099: }
100:
101: //////////////////////////////////////
102: // OTMConnection protocol
103: //////////////////////////////////////
104:
105: /**
106: * @see org.apache.ojb.otm.OTMConnection#getObjectByIdentity(Identity, int)
107: */
108: public Object getObjectByIdentity(Identity oid, int lock)
109: throws LockingException {
110: checkTransaction("getObjectByIdentity");
111: Object userObject;
112: Object cacheObject;
113:
114: cacheObject = _pb.getObjectByIdentity(oid);
115: if (cacheObject == null) {
116: // Possibly the object was inserted in this transaction
117: // and was not stored to database yet
118: userObject = _editingContext.lookup(oid);
119: } else {
120: userObject = getUserObject(oid, cacheObject);
121: // userObject from editing context may be proxy
122: userObject = ProxyHelper.getRealObject(userObject);
123: _editingContext.insert(oid, userObject, lock);
124: }
125: return userObject;
126: }
127:
128: private void checkTransaction(String methodBeingCalled) {
129: if (null == _tx) {
130: throw new TransactionNotInProgressException(
131: methodBeingCalled
132: + " requires a valid transaction. Please make sure you have created a new transaction, and called begin() on it.");
133: }
134: if (!_tx.isInProgress()) {
135: throw new TransactionNotInProgressException(
136: methodBeingCalled
137: + " cannot be called before transaction begin() is called");
138: }
139: }
140:
141: /**
142: * @see org.apache.ojb.otm.OTMConnection#getObjectByIdentity(Identity)
143: */
144: public Object getObjectByIdentity(Identity oid)
145: throws LockingException {
146: return getObjectByIdentity(oid, LockType.READ_LOCK);
147: }
148:
149: /**
150: * @param query The query to execute
151: * @return an Iterator that iterates Objects. The returned objects are locked for read.
152: */
153: public Iterator getIteratorByQuery(Query query) {
154: return getIteratorByQuery(query, LockType.READ_LOCK);
155: }
156:
157: /**
158: * @param query The query to execute
159: * @param lock the lock that need to be acquired on the object Possible values are:
160: * LockType.NO_LOCK (aka read only) - changes to the object will not be written to
161: * database; LockType.READ_LOCK (aka optimistic lock) - changes to the object will
162: * be written to the database, in this case the lock will be automatically upgraded
163: * to the write lock on transaction commit; LockType.WRITE_LOCK (aka pessimistic
164: * lock) - changes to the object will be written to the database.
165: * @return an Iterator that iterates Objects of class c if calling the .next() method. The
166: * returned objects are locked with the given lock value.
167: */
168: public Iterator getIteratorByQuery(Query query, int lock) {
169: checkTransaction("getIteratorByQuery");
170: return new OTMIterator((OJBIterator) _pb
171: .getIteratorByQuery(query), lock, null);
172: }
173:
174: /**
175: * @param query The OQL query to execute. Use this method if you don't want to load all the
176: * collection at once as OQLQuery.execute() does.
177: * @return an Iterator that iterates Objects. The returned objects are locked for read.
178: */
179: public Iterator getIteratorByOQLQuery(OQLQuery query) {
180: return getIteratorByOQLQuery(query, LockType.READ_LOCK);
181: }
182:
183: /**
184: * @param query The OQL query to execute. Use this method if you don't want to load all the
185: * collection at once as OQLQuery.execute() does.
186: * @return an Iterator that iterates Objects. The returned objects are locked for read.
187: */
188: public Iterator getIteratorByOQLQuery(OQLQuery query, int lock) {
189: checkTransaction("getIteratorByOQLQuery");
190: if (query instanceof OTMOQLQueryImpl) {
191: OTMOQLQueryImpl q = (OTMOQLQueryImpl) query;
192: return new OTMIterator((OJBIterator) _pb
193: .getIteratorByQuery(q.getQuery()), lock, q);
194: } else {
195: throw new IllegalArgumentException(
196: "The OQLQuery where created not via OTM API");
197: }
198: }
199:
200: /**
201: * @param query The query to execute
202: * @param lock the lock that need to be acquired on the object Possible values are:
203: * LockType.NO_LOCK (aka read only) - changes to the object will not be written to
204: * database; LockType.READ_LOCK (aka optimistic lock) - changes to the object will
205: * be written to the database, in this case the lock will be automatically upgraded
206: * to the write lock on transaction commit; LockType.WRITE_LOCK (aka pessimistic
207: * lock) - changes to the object will be written to the database.
208: * @return an Iterator that iterates Objects of class c if calling the .next() method. The
209: * returned objects are locked with the given lock value.
210: */
211: public Collection getCollectionByQuery(Query query, int lock) {
212: checkTransaction("getCollectionByQuery");
213: Collection col = _pb.getCollectionByQuery(query);
214: Collection result = createCollectionOfTheSameClass(col);
215: for (Iterator it = col.iterator(); it.hasNext();) {
216: result.add(insertObject(it.next(), lock));
217: }
218: return result;
219: }
220:
221: /**
222: * @param query The query to execute
223: * @return an Iterator that iterates Objects of class c if calling the .next() method. The
224: * returned objects are locked for read.
225: */
226: public Collection getCollectionByQuery(Query query) {
227: return getCollectionByQuery(query, LockType.READ_LOCK);
228: }
229:
230: /**
231: * Get the identity of the object
232: *
233: * @param object The object
234: * @return the identity of the object
235: */
236: public Identity getIdentity(Object object) {
237: return new Identity(object, _pb);
238: }
239:
240: /**
241: * Get the class descriptor
242: *
243: * @param clazz The class
244: * @return the descriptor of the class
245: */
246: public ClassDescriptor getDescriptorFor(Class clazz) {
247: return _pb.getClassDescriptor(clazz);
248: }
249:
250: /**
251: * @see org.apache.ojb.otm.OTMConnection#invalidate(Identity)
252: */
253: public void invalidate(Identity oid) throws LockingException {
254: if (null == _tx) {
255: throw new TransactionNotInProgressException(
256: "invalidate requires a valid transaction. Please make sure you have created a new transaction, and called begin() on it.");
257: }
258: // mark as invalidated in the editing context, if it's found there
259: _editingContext.insert(oid, null, LockType.READ_LOCK);
260:
261: // remove from the cache
262: _pb.serviceObjectCache().remove(oid);
263:
264: }
265:
266: /**
267: * @see org.apache.ojb.otm.OTMConnection#serviceObjectCache()
268: */
269: public ObjectCache serviceObjectCache() {
270: return _pb.serviceObjectCache();
271: }
272:
273: /**
274: * TODO remove all from editing context.
275: *
276: * @throws LockingException
277: */
278: public void invalidateAll() throws LockingException {
279: _pb.serviceObjectCache().clear();
280: }
281:
282: /**
283: * @see org.apache.ojb.otm.OTMConnection#lockForWrite(Object)
284: */
285: public void lockForWrite(Object object) throws LockingException {
286: checkTransaction("lockForWrite");
287: makePersistent(object);
288: }
289:
290: /**
291: * @see org.apache.ojb.otm.OTMConnection#makePersistent(Object)
292: */
293: public void makePersistent(Object userObject)
294: throws LockingException {
295: checkTransaction("makePersistent");
296: Identity oid = new Identity(userObject, _pb);
297: Object cacheObject = _pb.getObjectByIdentity(oid);
298:
299: if ((cacheObject != null)
300: && (_editingContext.lookup(oid) == null)) {
301: // The object exists in the database, but is not yet in the editing
302: // context, so we need to put it to the editing context in its
303: // old state, then we will put the modified userObject.
304: // This will allow the editing context to find changes
305: ObjectCopyStrategy copyStrategy = _tx.getKit()
306: .getCopyStrategy(oid);
307: Object origUserObject = copyStrategy.copy(cacheObject, _pb);
308: _editingContext.insert(oid, origUserObject,
309: LockType.WRITE_LOCK);
310: }
311: _editingContext.insert(oid, userObject, LockType.WRITE_LOCK);
312: }
313:
314: /**
315: * @see org.apache.ojb.otm.OTMConnection#deletePersistent(Object)
316: */
317: public void deletePersistent(Object userObject)
318: throws LockingException {
319: checkTransaction("deletePersistent");
320: Identity oid = new Identity(userObject, _pb);
321: Object cacheObject = _pb.getObjectByIdentity(oid);
322: if (cacheObject == null) {
323: // Possibly the object was inserted in this transaction
324: // and was not stored to database yet, so we simply remove it
325: // from editing context.
326: _editingContext.remove(oid);
327: } else {
328: if (_editingContext.lookup(oid) == null) {
329: // The object exists in the database, but is not yet in the editing
330: // context, so we need to put it to the editing context
331: ObjectCopyStrategy copyStrategy = _tx.getKit()
332: .getCopyStrategy(oid);
333: Object origUserObject = copyStrategy.copy(cacheObject,
334: _pb);
335: _editingContext.insert(oid, origUserObject,
336: LockType.WRITE_LOCK);
337: }
338: _editingContext.deletePersistent(oid, userObject);
339: }
340: }
341:
342: /**
343: * @see org.apache.ojb.otm.OTMConnection#refresh(Object)
344: */
345: public void refresh(Object userObject) {
346: checkTransaction("refresh");
347: Identity oid = new Identity(userObject, _pb);
348: _editingContext.refresh(oid, userObject);
349: }
350:
351: public EditingContext getEditingContext() {
352: return _editingContext;
353: }
354:
355: public EnhancedOQLQuery newOQLQuery() {
356: return newOQLQuery(LockType.READ_LOCK);
357: }
358:
359: public EnhancedOQLQuery newOQLQuery(int lock) {
360: checkTransaction("newOQLQuery");
361: OQLQueryImpl query = new OTMOQLQueryImpl(_pb.getPBKey(), lock);
362: try {
363: m_configurator.configure(query);
364: } catch (ConfigurationException e) {
365: throw new ODMGRuntimeException(
366: "Error in configuration of OQLQueryImpl instance: "
367: + e.getMessage());
368: }
369: return query;
370: }
371:
372: public int getCount(Query query) {
373: checkTransaction("getCount");
374: return _pb.getCount(query);
375: }
376:
377: private Object insertObject(Object cacheObject, int lock) {
378: Object ctxObject;
379: Identity oid;
380: Object userObject;
381:
382: oid = getIdentity(cacheObject);
383: userObject = getUserObject(oid, cacheObject);
384: try {
385: _editingContext.insert(oid, userObject, lock);
386: } catch (LockingException ex) {
387: throw new LockingPassthruException(ex);
388: }
389:
390: return userObject;
391: }
392:
393: /**
394: * Get user object (from the editing context) with the given oid.
395: * If not found, then create it as a copy of cacheObject.
396: * User object and cache object must be separate.
397: * @param oid The identity
398: * @param cacheObject the object for user
399: */
400: private Object getUserObject(Identity oid, Object cacheObject) {
401: Object userObject = _editingContext.lookup(oid);
402:
403: if (userObject == null) {
404: ObjectCopyStrategy copyStrategy = _tx.getKit()
405: .getCopyStrategy(oid);
406: userObject = copyStrategy.copy(cacheObject, _pb);
407: }
408: return userObject;
409: }
410:
411: private Collection createCollectionOfTheSameClass(Collection col) {
412: try {
413: return (Collection) col.getClass().newInstance();
414: } catch (Throwable ex) {
415: return new ArrayList();
416: }
417: }
418:
419: ///////////////////////////////////////
420: // Transaction Notifications
421: ///////////////////////////////////////
422:
423: /**
424: *
425: * Notification issued by the driving transaction to begin this transaction
426: *
427: */
428: public abstract void transactionBegin() throws TransactionException;
429:
430: /**
431: *
432: * Prepare for a commit. As part of a two phase commit protocol of the transaction.
433: *
434: */
435: public abstract void transactionPrepare()
436: throws TransactionException;
437:
438: /**
439: *
440: * Notification issued by the driving transaction to commit resources held by this connection.
441: *
442: */
443: public abstract void transactionCommit()
444: throws TransactionException;
445:
446: /**
447: *
448: * Notification issued by the driving transaction to rollback resources held by this
449: * connection.
450: *
451: */
452: public abstract void transactionRollback()
453: throws TransactionException;
454:
455: ///////////////////////////////////////
456: // Inner classes
457: ///////////////////////////////////////
458:
459: private class OTMIterator implements OJBIterator {
460: private final OJBIterator _it;
461: private final int _lock;
462: private final OTMOQLQueryImpl _oqlQuery;
463:
464: OTMIterator(OJBIterator it, int lock, OTMOQLQueryImpl oqlQuery) {
465: _it = it;
466: _lock = lock;
467: _oqlQuery = oqlQuery;
468: }
469:
470: public boolean hasNext() {
471: boolean res = _it.hasNext();
472:
473: // once the result set is finished, close it
474: if (!res) {
475: done();
476: }
477:
478: return res;
479: }
480:
481: public Object next() {
482: Object object = _it.next();
483: object = insertObject(object, _lock);
484: return object;
485: }
486:
487: public void remove() {
488: throw new UnsupportedOperationException();
489: }
490:
491: public void done() {
492: releaseDbResources();
493: if (_oqlQuery != null) {
494: _oqlQuery.resetBindIterator();
495: }
496: }
497:
498: protected void finalize() {
499: done();
500: }
501:
502: /*
503: * (non-Javadoc)
504: *
505: * @see org.apache.ojb.broker.accesslayer.OJBIterator#absolute(int)
506: */
507: public boolean absolute(int row)
508: throws PersistenceBrokerException {
509: return _it.absolute(row);
510: }
511:
512: /*
513: * (non-Javadoc)
514: *
515: * @see org.apache.ojb.broker.accesslayer.OJBIterator#fullSize()
516: */
517: public int fullSize() throws PersistenceBrokerException {
518: return _it.fullSize();
519: }
520:
521: /*
522: * (non-Javadoc)
523: *
524: * @see org.apache.ojb.broker.accesslayer.OJBIterator#relative(int)
525: */
526: public boolean relative(int row)
527: throws PersistenceBrokerException {
528: return _it.relative(row);
529: }
530:
531: /*
532: * (non-Javadoc)
533: *
534: * @see org.apache.ojb.broker.accesslayer.OJBIterator#releaseDbResources()
535: */
536: public void releaseDbResources() {
537: _it.releaseDbResources();
538: }
539:
540: /*
541: * (non-Javadoc)
542: *
543: * @see org.apache.ojb.broker.accesslayer.OJBIterator#size()
544: */
545: public int size() throws PersistenceBrokerException {
546: return _it.size();
547: }
548:
549: /**
550: * @see org.apache.ojb.broker.accesslayer.OJBIterator#disableLifeCycleEvents()
551: */
552: public void disableLifeCycleEvents() {
553: _it.disableLifeCycleEvents();
554: }
555: }
556:
557: private class OTMOQLQueryImpl extends OQLQueryImpl {
558: int _lock;
559:
560: public OTMOQLQueryImpl(PBKey key, int lock) {
561: super (key);
562: _lock = lock;
563: }
564:
565: /**
566: * Execute the query. After executing a query, the parameter list is reset.
567: *
568: * @return The object that represents the result of the query. The returned data, whatever
569: * its OQL type, is encapsulated into an object. For instance, when OQL returns an
570: * integer, the result is put into an <code>Integer</code> object. When OQL
571: * returns a collection (literal or object), the result is always a Java collection
572: * object of the same kind (for instance, a <code>DList</code>).
573: * @exception org.odmg.QueryException An exception has occurred while executing the query.
574: */
575: public Object execute() throws org.odmg.QueryException {
576: Collection result;
577: Iterator iter = null;
578: Query query = getQuery();
579:
580: try {
581: if (!(query instanceof ReportQuery)) {
582: Collection res0 = _pb.getCollectionByQuery(query);
583: result = createCollectionOfTheSameClass(res0);
584: for (iter = res0.iterator(); iter.hasNext();) {
585: result.add(insertObject(iter.next(), _lock));
586: }
587: } else {
588: result = new ArrayList();
589: iter = _pb.getReportQueryIteratorByQuery(query);
590: while (iter.hasNext()) {
591: Object[] res = (Object[]) iter.next();
592:
593: if (res.length == 1) {
594: if (res[0] != null) // skip null values
595: {
596: result.add(res[0]);
597: }
598: } else {
599: // skip null tuples
600: for (int i = 0; i < res.length; i++) {
601: if (res[i] != null) {
602: result.add(res);
603: break;
604: }
605: }
606: }
607: }
608: }
609: resetBindIterator();
610: } finally {
611: if ((iter != null) && (iter instanceof OJBIterator)) {
612: ((OJBIterator) iter).releaseDbResources();
613: }
614: }
615: return result;
616: }
617:
618: void resetBindIterator() {
619: // reset iterator to start of list so we can reuse this query
620: ListIterator it = getBindIterator();
621: while (it.hasPrevious()) {
622: it.previous();
623: }
624: }
625: }
626:
627: }
|