KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > core > proxy > CollectionProxyDefaultImpl


1 package org.apache.ojb.broker.core.proxy;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Collection JavaDoc;
20 import java.util.Iterator JavaDoc;
21
22 import org.apache.ojb.broker.ManageableCollection;
23 import org.apache.ojb.broker.OJBRuntimeException;
24 import org.apache.ojb.broker.PBFactoryException;
25 import org.apache.ojb.broker.PBKey;
26 import org.apache.ojb.broker.PersistenceBroker;
27 import org.apache.ojb.broker.PersistenceBrokerException;
28 import org.apache.ojb.broker.PersistenceBrokerFactory;
29 import org.apache.ojb.broker.metadata.MetadataManager;
30 import org.apache.ojb.broker.metadata.MetadataException;
31 import org.apache.ojb.broker.core.PersistenceBrokerThreadMapping;
32 import org.apache.ojb.broker.query.Query;
33 import org.apache.ojb.broker.util.collections.IRemovalAwareCollection;
34 import org.apache.ojb.broker.util.collections.RemovalAwareCollection;
35
36 /**
37  * A place holder for a whole collection to support deferred loading of relationships.
38  * The complete collection is loaded on access to the data.
39  *
40  * @author <a HREF="mailto:jbraeuchi@hotmail.com">Jakob Braeuchi</a>
41  * @version $Id: CollectionProxyDefaultImpl.java,v 1.7.2.7 2005/12/21 22:25:30 tomdz Exp $
42  */

43 public class CollectionProxyDefaultImpl implements Collection JavaDoc, ManageableCollection, CollectionProxy
44 {
45     /** The key for acquiring the above broker */
46     private PBKey _brokerKey;
47     /** Flag set when per-thread metadata profiles are in use. */
48     private boolean _perThreadDescriptorsEnabled;
49     /** Profile key used when lazy-loading with per-thread metadata profiles. */
50     private Object JavaDoc _profileKey;
51     /** The query that defines the values in the collection */
52     private Query _query;
53     /** The actual data (if already loaded) */
54     private Collection JavaDoc _data;
55     /** The collection type */
56     private Class JavaDoc _collectionClass;
57     /** The number of objects */
58     private int _size = -1;
59     /*
60     arminw
61     fix a bug, caused by closing PB instances
62     obtained from PersistenceBrokerThreadMapping.
63     TODO: Could we find a better solution for this?
64     */

65     private boolean _needsClose;
66     /** Objects that listen on this proxy for loading events */
67     private transient ArrayList JavaDoc _listeners;
68
69     /**
70      * Creates a new collection proxy (uses
71      * {@link org.apache.ojb.broker.util.collections.RemovalAwareCollection}
72      * as the collection class).
73      *
74      * @param brokerKey The key of the persistence broker
75      * @param query The defining query
76      */

77     public CollectionProxyDefaultImpl(PBKey brokerKey, Query query)
78     {
79         this(brokerKey, RemovalAwareCollection.class, query);
80     }
81
82     /**
83      * Creates a new collection proxy that uses the given collection type.
84      *
85      * @param brokerKey The key of the persistence broker
86      * @param collClass The collection type
87      * @param query The defining query
88      */

89     public CollectionProxyDefaultImpl(PBKey brokerKey, Class JavaDoc collClass, Query query)
90     {
91         MetadataManager mm = MetadataManager.getInstance();
92         _perThreadDescriptorsEnabled = mm.isEnablePerThreadChanges();
93         if (_perThreadDescriptorsEnabled)
94         {
95             // mkalen: To minimize memory footprint we remember only the OJB profile key
96
// (instead of all active class-mappings).
97
final Object JavaDoc key = mm.getCurrentProfileKey();
98             if (key == null)
99             {
100                 // mkalen: Unsupported: using proxies with per-thread metadata changes without profile keys.
101
throw new MetadataException("Trying to create a Collection proxy with per-thread metadata changes enabled, but no profile key.");
102             }
103             setProfileKey(key);
104         }
105         setBrokerKey(brokerKey);
106         setCollectionClass(collClass);
107         setQuery(query);
108     }
109
110     /**
111      * Reactivates metadata profile used when creating proxy, if needed.
112      * Calls to this method should be guarded by checking
113      * {@link #_perThreadDescriptorsEnabled} since the profile never
114      * needs to be reloaded if not using pre-thread metadata changes.
115      */

116     protected void loadProfileIfNeeded()
117     {
118         final Object JavaDoc key = getProfileKey();
119         if (key != null)
120         {
121             final MetadataManager mm = MetadataManager.getInstance();
122             if (!key.equals(mm.getCurrentProfileKey()))
123             {
124                 mm.loadProfile(key);
125             }
126         }
127     }
128
129     /**
130      * Determines whether the collection data already has been loaded from the database.
131      *
132      * @return <code>true</code> if the data is already loaded
133      */

134     public boolean isLoaded()
135     {
136         return _data != null;
137     }
138
139     /**
140      * Determines the number of elements that the query would return. Override this
141      * method if the size shall be determined in a specific way.
142      *
143      * @return The number of elements
144      */

145     protected synchronized int loadSize() throws PersistenceBrokerException
146     {
147         PersistenceBroker broker = getBroker();
148         try
149         {
150             return broker.getCount(getQuery());
151         }
152         catch (Exception JavaDoc ex)
153         {
154             throw new PersistenceBrokerException(ex);
155         }
156         finally
157         {
158             releaseBroker(broker);
159         }
160     }
161
162     /**
163      * Sets the size internally.
164      *
165      * @param size The new size
166      */

167     protected synchronized void setSize(int size)
168     {
169         _size = size;
170     }
171     
172     /**
173      * Loads the data from the database. Override this method if the objects
174      * shall be loaded in a specific way.
175      *
176      * @return The loaded data
177      */

178     protected Collection JavaDoc loadData() throws PersistenceBrokerException
179     {
180         PersistenceBroker broker = getBroker();
181         try
182         {
183             Collection JavaDoc result;
184
185             if (_data != null) // could be set by listener
186
{
187                 result = _data;
188             }
189             else if (_size != 0)
190             {
191                 // TODO: returned ManageableCollection should extend Collection to avoid
192
// this cast
193
result = (Collection JavaDoc) broker.getCollectionByQuery(getCollectionClass(), getQuery());
194             }
195             else
196             {
197                 result = (Collection JavaDoc)getCollectionClass().newInstance();
198             }
199             return result;
200         }
201         catch (Exception JavaDoc ex)
202         {
203             throw new PersistenceBrokerException(ex);
204         }
205         finally
206         {
207             releaseBroker(broker);
208         }
209     }
210
211     /**
212      * Notifies all listeners that the data is about to be loaded.
213      */

214     protected void beforeLoading()
215     {
216         if (_listeners != null)
217         {
218             CollectionProxyListener listener;
219
220             if (_perThreadDescriptorsEnabled) {
221                 loadProfileIfNeeded();
222             }
223             for (int idx = _listeners.size() - 1; idx >= 0; idx--)
224             {
225                 listener = (CollectionProxyListener)_listeners.get(idx);
226                 listener.beforeLoading(this);
227             }
228         }
229     }
230
231     /**
232      * Notifies all listeners that the data has been loaded.
233      */

234     protected void afterLoading()
235     {
236         if (_listeners != null)
237         {
238             CollectionProxyListener listener;
239
240             if (_perThreadDescriptorsEnabled) {
241                 loadProfileIfNeeded();
242             }
243             for (int idx = _listeners.size() - 1; idx >= 0; idx--)
244             {
245                 listener = (CollectionProxyListener)_listeners.get(idx);
246                 listener.afterLoading(this);
247             }
248         }
249     }
250
251     /**
252      * @see Collection#size()
253      */

254     public int size()
255     {
256         if (isLoaded())
257         {
258             return getData().size();
259         }
260         else
261         {
262             if (_size < 0)
263             {
264                 _size = loadSize();
265             }
266             return _size;
267         }
268     }
269
270     /**
271      * @see Collection#isEmpty()
272      */

273     public boolean isEmpty()
274     {
275         return size() == 0;
276     }
277
278     /**
279      * @see Collection#contains(Object)
280      */

281     public boolean contains(Object JavaDoc o)
282     {
283         return getData().contains(o);
284     }
285
286     /**
287      * @see Collection#iterator()
288      */

289     public Iterator JavaDoc iterator()
290     {
291         return getData().iterator();
292     }
293
294     /**
295      * @see Collection#toArray()
296      */

297     public Object JavaDoc[] toArray()
298     {
299         return getData().toArray();
300     }
301
302     /**
303      * @see Collection#toArray(Object[])
304      */

305     public Object JavaDoc[] toArray(Object JavaDoc[] a)
306     {
307         return getData().toArray(a);
308     }
309
310     /**
311      * @see Collection#add(Object)
312      */

313     public boolean add(Object JavaDoc o)
314     {
315         return getData().add(o);
316     }
317
318     /**
319      * @see Collection#remove(Object)
320      */

321     public boolean remove(Object JavaDoc o)
322     {
323         return getData().remove(o);
324     }
325
326     /**
327      * @see Collection#containsAll(Collection)
328      */

329     public boolean containsAll(Collection JavaDoc c)
330     {
331         return getData().containsAll(c);
332     }
333
334     /**
335      * @see Collection#addAll(Collection)
336      */

337     public boolean addAll(Collection JavaDoc c)
338     {
339         return getData().addAll(c);
340     }
341
342     /**
343      * @see Collection#removeAll(Collection)
344      */

345     public boolean removeAll(Collection JavaDoc c)
346     {
347         return getData().removeAll(c);
348     }
349
350     /**
351      * @see Collection#retainAll(Collection)
352      */

353     public boolean retainAll(Collection JavaDoc c)
354     {
355         return getData().retainAll(c);
356     }
357
358     /**
359      * Clears the proxy. A cleared proxy is defined as loaded
360      *
361      * @see Collection#clear()
362      */

363     public void clear()
364     {
365         Class JavaDoc collClass = getCollectionClass();
366
367         // ECER: assure we notify all objects being removed,
368
// necessary for RemovalAwareCollections...
369
if (IRemovalAwareCollection.class.isAssignableFrom(collClass))
370         {
371             getData().clear();
372         }
373         else
374         {
375             Collection JavaDoc coll;
376             // BRJ: use an empty collection so isLoaded will return true
377
// for non RemovalAwareCollections only !!
378
try
379             {
380                 coll = (Collection JavaDoc) collClass.newInstance();
381             }
382             catch (Exception JavaDoc e)
383             {
384                 coll = new ArrayList JavaDoc();
385             }
386
387             setData(coll);
388         }
389         _size = 0;
390     }
391
392     /**
393      * Returns the defining query.
394      *
395      * @return The query
396      */

397     public Query getQuery()
398     {
399         return _query;
400     }
401
402     /**
403      * Sets the defining query.
404      *
405      * @param query The query
406      */

407     protected void setQuery(Query query)
408     {
409         _query = query;
410     }
411
412     /**
413      * Release the broker instance.
414      */

415     protected synchronized void releaseBroker(PersistenceBroker broker)
416     {
417         /*
418         arminw:
419         only close the broker instance if we get
420         it from the PBF, do nothing if we obtain it from
421         PBThreadMapping
422         */

423         if (broker != null && _needsClose)
424         {
425             _needsClose = false;
426             broker.close();
427         }
428     }
429
430     /**
431      * Acquires a broker instance. If no PBKey is available a runtime exception will be thrown.
432      *
433      * @return A broker instance
434      */

435     protected synchronized PersistenceBroker getBroker() throws PBFactoryException
436     {
437         /*
438             mkalen:
439             NB! The loadProfileIfNeeded must be called _before_ acquiring a broker below,
440             since some methods in PersistenceBrokerImpl will keep a local reference to
441             the descriptor repository that was active during broker construction/refresh
442             (not checking the repository beeing used on method invocation).
443
444             PersistenceBrokerImpl#getClassDescriptor(Class clazz) is such a method,
445             that will throw ClassNotPersistenceCapableException on the following scenario:
446
447             (All happens in one thread only):
448             t0: activate per-thread metadata changes
449             t1: load, register and activate profile A
450             t2: load object O1 witch collection proxy C to objects {O2} (C stores profile key K(A))
451             t3: close broker from t2
452             t4: load, register and activate profile B
453             t5: reference O1.getO2Collection, causing C loadData() to be invoked
454             t6: C calls getBroker
455                 broker B is created and descriptorRepository is set to descriptors from profile B
456             t7: C calls loadProfileIfNeeded, re-activating profile A
457             t8: C calls B.getCollectionByQuery
458             t9: B gets callback (via QueryReferenceBroker) to getClassDescriptor
459                 the local descriptorRepository from t6 is used!
460                 => We will now try to query for {O2} with profile B
461                     (even though we re-activated profile A in t7)
462                     => ClassNotPersistenceCapableException
463
464             Keeping loadProfileIfNeeded() at the start of this method changes everything from t6:
465             t6: C calls loadProfileIfNeeded, re-activating profile A
466             t7: C calls getBroker,
467                 broker B is created and descriptorRepository is set to descriptors from profile A
468             t8: C calls B.getCollectionByQuery
469             t9: B gets callback to getClassDescriptor,
470                 the local descriptorRepository from t6 is used
471                 => We query for {O2} with profile A
472                     => All good :-)
473         */

474         if (_perThreadDescriptorsEnabled)
475         {
476             loadProfileIfNeeded();
477         }
478
479         PersistenceBroker broker;
480         if (getBrokerKey() == null)
481         {
482             /*
483             arminw:
484             if no PBKey is set we throw an exception, because we don't
485             know which PB (connection) should be used.
486             */

487             throw new OJBRuntimeException("Can't find associated PBKey. Need PBKey to obtain a valid" +
488                                           "PersistenceBroker instance from intern resources.");
489         }
490         // first try to use the current threaded broker to avoid blocking
491
broker = PersistenceBrokerThreadMapping.currentPersistenceBroker(getBrokerKey());
492         // current broker not found or was closed, create a intern new one
493
if (broker == null || broker.isClosed())
494         {
495             broker = PersistenceBrokerFactory.createPersistenceBroker(getBrokerKey());
496             // signal that we use a new internal obtained PB instance to read the
497
// data and that this instance have to be closed after use
498
_needsClose = true;
499         }
500         return broker;
501     }
502
503     /**
504      * Returns the collection data, load it if not already done so.
505      *
506      * @return The data
507      */

508     public synchronized Collection JavaDoc getData()
509     {
510         if (!isLoaded())
511         {
512             beforeLoading();
513             setData(loadData());
514             afterLoading();
515         }
516         return _data;
517     }
518
519     /**
520      * Sets the collection data.
521      *
522      * @param data The data
523      */

524     public void setData(Collection JavaDoc data)
525     {
526         _data = data;
527     }
528
529     /**
530      * Returns the collection type.
531      *
532      * @return The collection type
533      */

534     public Class JavaDoc getCollectionClass()
535     {
536         return _collectionClass;
537     }
538
539     /**
540      * Sets the collection type.
541      *
542      * @param collClass The collection type
543      */

544     protected void setCollectionClass(Class JavaDoc collClass)
545     {
546         _collectionClass = collClass;
547     }
548
549     /**
550      * @see org.apache.ojb.broker.ManageableCollection#ojbAdd(Object)
551      */

552     public void ojbAdd(Object JavaDoc anObject)
553     {
554         add(anObject);
555     }
556
557     /**
558      * @see org.apache.ojb.broker.ManageableCollection#ojbAddAll(ManageableCollection)
559      */

560     public void ojbAddAll(ManageableCollection otherCollection)
561     {
562         addAll((CollectionProxyDefaultImpl)otherCollection);
563     }
564
565     /**
566      * @see org.apache.ojb.broker.ManageableCollection#ojbIterator()
567      */

568     public Iterator JavaDoc ojbIterator()
569     {
570         return iterator();
571     }
572
573     /**
574      * @see org.apache.ojb.broker.ManageableCollection#afterStore(PersistenceBroker broker)
575      */

576     public void afterStore(PersistenceBroker broker) throws PersistenceBrokerException
577     {
578         // If the real subject is a ManageableCollection
579
// the afterStore() callback must be invoked !
580
Collection JavaDoc c = getData();
581
582         if (c instanceof ManageableCollection)
583         {
584             ((ManageableCollection)c).afterStore(broker);
585         }
586     }
587
588     /**
589      * Returns the key of the persistence broker used by this collection.
590      *
591      * @return The broker key
592      */

593     public PBKey getBrokerKey()
594     {
595         return _brokerKey;
596     }
597
598     /**
599      * Sets the key of the persistence broker used by this collection.
600      *
601      * @param brokerKey The key of the broker
602      */

603     protected void setBrokerKey(PBKey brokerKey)
604     {
605         _brokerKey = brokerKey;
606     }
607
608     /**
609      * Returns the metadata profile key used when creating this proxy.
610      *
611      * @return brokerKey The key of the broker
612      */

613     protected Object JavaDoc getProfileKey()
614     {
615         return _profileKey;
616     }
617
618     /**
619      * Sets the metadata profile key used when creating this proxy.
620      *
621      * @param profileKey the metadata profile key
622      */

623     public void setProfileKey(Object JavaDoc profileKey)
624     {
625         _profileKey = profileKey;
626     }
627
628     /**
629      * Adds a listener to this collection.
630      *
631      * @param listener The listener to add
632      */

633     public synchronized void addListener(CollectionProxyListener listener)
634     {
635         if (_listeners == null)
636         {
637             _listeners = new ArrayList JavaDoc();
638         }
639         // to avoid multi-add of same listener, do check
640
if(!_listeners.contains(listener))
641         {
642             _listeners.add(listener);
643         }
644     }
645
646     /**
647      * Removes the given listener from this collecton.
648      *
649      * @param listener The listener to remove
650      */

651     public synchronized void removeListener(CollectionProxyListener listener)
652     {
653         if (_listeners != null)
654         {
655             _listeners.remove(listener);
656         }
657     }
658
659 }
660
Popular Tags