Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/access/IncrementalFaultList.java


1   /* ====================================================================
2    * 
3    * The ObjectStyle Group Software License, Version 1.0 
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group 
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer. 
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:  
22   *       "This product includes software developed by the 
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne" 
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written 
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */
56  package org.objectstyle.cayenne.access;
57  
58  import java.util.ArrayList;
59  import java.util.Collection;
60  import java.util.Collections;
61  import java.util.Iterator;
62  import java.util.List;
63  import java.util.ListIterator;
64  import java.util.Map;
65  
66  import org.objectstyle.cayenne.CayenneException;
67  import org.objectstyle.cayenne.CayenneRuntimeException;
68  import org.objectstyle.cayenne.DataObject;
69  import org.objectstyle.cayenne.exp.Expression;
70  import org.objectstyle.cayenne.exp.ExpressionFactory;
71  import org.objectstyle.cayenne.map.ObjEntity;
72  import org.objectstyle.cayenne.query.GenericSelectQuery;
73  import org.objectstyle.cayenne.query.SelectQuery;
74  
75  /**
76   * A synchronized list that serves as a container of DataObjects. It is returned
77   * when a paged query is performed by DataContext. On creation, only the first
78   * "page" is fully resolved, for the rest of the objects only their ObjectIds
79   * are read. Pages following the first page are resolved on demand only. On
80   * access to an element, the list would ensure that this element as well as all
81   * its siblings on the same page are fully resolved.
82   * 
83   * <p>Note that this list would only allow addition of DataObjects. Attempts to
84   * add any other object types will result in an exception.</p>
85   * 
86   * <p>Performance note: certain operations like <code>toArray</code> would
87   * trigger full list fetch.</p>
88   * 
89   * @author Andrei Adamchik
90   */
91  public class IncrementalFaultList implements List {
92  
93      protected int pageSize;
94      protected List elements;
95      protected DataContext dataContext;
96      protected ObjEntity rootEntity;
97      protected SelectQuery internalQuery;
98      protected int unfetchedObjects;
99  
100     /**
101      * Creates a new list copying settings from another list.
102      * Elements WILL NOT be copied or fetched.
103      */
104     public IncrementalFaultList(IncrementalFaultList list) {
105         this.pageSize = list.pageSize;
106         this.internalQuery = list.internalQuery;
107         this.dataContext = list.dataContext;
108         this.rootEntity = list.rootEntity;
109         elements = Collections.synchronizedList(new ArrayList());
110     }
111 
112     public IncrementalFaultList(
113         DataContext dataContext,
114         GenericSelectQuery query) {
115         if (query.getPageSize() <= 0) {
116             throw new CayenneRuntimeException(
117                 "IncrementalFaultList does not support unpaged queries. Query page size is "
118                     + query.getPageSize());
119         }
120 
121         this.elements = Collections.synchronizedList(new ArrayList());
122         this.dataContext = dataContext;
123         this.pageSize = query.getPageSize();
124         this.rootEntity =
125             dataContext.getEntityResolver().lookupObjEntity(query);
126 
127         // create an internal query, it is a partial replica of 
128         // the original query and will serve as a value holder for 
129         // various parameters
130         this.internalQuery = new SelectQuery();
131         this.internalQuery.setRoot(query.getRoot());
132         this.internalQuery.setLoggingLevel(query.getLoggingLevel());
133         if (query instanceof SelectQuery) {
134             this.internalQuery.addPrefetches(((SelectQuery) query).getPrefetches());
135         }
136 
137         fillIn(query);
138     }
139 
140     /**
141      * Performs initialization of the internal list of objects.
142      * Only the first page is fully resolved. For the rest of
143      * the list, only ObjectIds are read.
144      */
145     protected void fillIn(GenericSelectQuery query) {
146         synchronized (elements) {
147 
148             // start fresh
149             elements.clear();
150 
151             try {
152                 long t1 = System.currentTimeMillis();
153                 ResultIterator it = dataContext.performIteratedQuery(query);
154                 try {
155                     // read first page completely, the rest as ObjectIds
156                     for (int i = 0; i < pageSize && it.hasNextRow(); i++) {
157                         Map row = it.nextDataRow();
158                         elements.add(
159                             dataContext.objectFromDataRow(
160                                 rootEntity,
161                                 row,
162                                 true));
163                     }
164 
165                     // continue reading ids
166                     while (it.hasNextRow()) {
167                         elements.add(it.nextObjectId());
168                     }
169 
170                     QueryLogger.logSelectCount(
171                         query.getLoggingLevel(),
172                         elements.size(),
173                         System.currentTimeMillis() - t1);
174 
175                 } finally {
176                     it.close();
177                 }
178             } catch (CayenneException e) {
179                 throw new CayenneRuntimeException("Error performing query.", e);
180             }
181 
182             // process prefetching
183             if (internalQuery.getPrefetches().size() > 0) {
184                 int endOfPage =
185                     (elements.size() < pageSize) ? elements.size() : pageSize;
186                 dataContext.prefetchRelationships(
187                     internalQuery,
188                     elements.subList(0, endOfPage));
189             }
190 
191             unfetchedObjects = elements.size() - pageSize;
192         }
193     }
194 
195     /**
196      * Will resolve all unread objects.
197      */
198     public void resolveAll() {
199         resolveInterval(0, size());
200     }
201 
202     /**
203      * Resolves a sublist of objects starting at <code>fromIndex</code>
204      * up to but not including <code>toIndex</code>. Internally performs
205      * bound checking and trims indexes accordingly.
206      */
207     protected void resolveInterval(int fromIndex, int toIndex) {
208         if (fromIndex >= toIndex) {
209             return;
210         }
211 
212         synchronized (elements) {
213             if (elements.size() == 0) {
214                 return;
215             }
216 
217             // perform bound checking
218             if (fromIndex < 0) {
219                 fromIndex = 0;
220             }
221 
222             if (toIndex > elements.size()) {
223                 toIndex = elements.size();
224             }
225 
226             List quals = new ArrayList(pageSize);
227             List ids = new ArrayList(pageSize);
228             for (int i = fromIndex; i < toIndex; i++) {
229                 Object obj = elements.get(i);
230                 if (obj instanceof Map) {
231                     ids.add(obj);
232                     quals.add(
233                         ExpressionFactory.matchAllDbExp(
234                             (Map) obj,
235                             Expression.EQUAL_TO));
236                 }
237             }
238 
239             if (quals.size() == 0) {
240                 return;
241             }
242 
243             SelectQuery query =
244                 new SelectQuery(
245                     rootEntity.getName(),
246                     ExpressionFactory.joinExp(Expression.OR, quals));
247 
248             List objects = dataContext.performQuery(query);
249 
250             // sanity check - database data may have changed
251             if (objects.size() < ids.size()) {
252                 // find missing ids
253                 StringBuffer buf = new StringBuffer();
254                 buf.append("Some ObjectIds are missing from the database. ");
255                 buf.append("Expected ").append(ids.size()).append(
256                     ", fetched ").append(
257                     objects.size());
258 
259                 Iterator idsIt = ids.iterator();
260                 boolean first = true;
261                 while (idsIt.hasNext()) {
262                     boolean found = false;
263                     Object id = idsIt.next();
264                     Iterator oIt = objects.iterator();
265                     while (oIt.hasNext()) {
266                         if (((DataObject) oIt.next())
267                             .getObjectId()
268                             .getIdSnapshot()
269                             .equals(id)) {
270                             found = true;
271                             break;
272                         }
273                     }
274 
275                     if (!found) {
276                         if (first) {
277                             first = false;
278                         } else {
279                             buf.append(", ");
280                         }
281 
282                         buf.append(id.toString());
283                     }
284                 }
285 
286                 throw new CayenneRuntimeException(buf.toString());
287             } else if (objects.size() > ids.size()) {
288                 throw new CayenneRuntimeException(
289                     "Expected "
290                         + ids.size()
291                         + " objects, retrieved "
292                         + objects.size());
293             }
294 
295             // replace ids in the list with objects
296             Iterator it = objects.iterator();
297             while (it.hasNext()) {
298                 DataObject obj = (DataObject) it.next();
299                 Map idMap = obj.getObjectId().getIdSnapshot();
300 
301                 boolean found = false;
302                 for (int i = fromIndex; i < toIndex; i++) {
303                     if (idMap.equals(elements.get(i))) {
304                         elements.set(i, obj);
305                         found = true;
306                         break;
307                     }
308                 }
309 
310                 if (!found) {
311                     throw new CayenneRuntimeException(
312                         "Can't find id for " + idMap);
313                 }
314             }
315 
316             unfetchedObjects -= objects.size();
317         }
318 
319         // process prefetching
320         if (internalQuery.getPrefetches().size() > 0) {
321             int endOfPage =
322                 (elements.size() < toIndex) ? elements.size() : toIndex;
323             dataContext.prefetchRelationships(
324                 internalQuery,
325                 elements.subList(fromIndex, endOfPage));
326         }
327     }
328 
329     public int pageIndex(int elementIndex) {
330         if (pageSize <= 0 || elementIndex < 0) {
331             return -1;
332         }
333 
334         return elementIndex / pageSize;
335     }
336 
337     /**
338      * Returns the dataContext.
339      * @return DataContext
340      */
341     public DataContext getDataContext() {
342         return dataContext;
343     }
344 
345     /**
346      * Returns the pageSize.
347      * @return int
348      */
349     public int getPageSize() {
350         return pageSize;
351     }
352 
353     /**
354      * This method would resolve all unresolved objects and then return
355      * a list iterator over an internal list.
356      */
357     public ListIterator listIterator() {
358         resolveAll();
359         return elements.listIterator();
360     }
361 
362     /**
363      * This method would resolve all unresolved objects and then return
364      * a list iterator over an internal list.
365      */
366     public ListIterator listIterator(int index) {
367         resolveAll();
368         return elements.listIterator(index);
369     }
370 
371     /**
372      * This method would resolve all unresolved objects and then return
373      * an iterator over an internal list.
374      */
375     public Iterator iterator() {
376         resolveAll();
377         return elements.iterator();
378     }
379 
380     /**
381      * @see java.util.List#add(int, Object)
382      */
383     public void add(int index, Object element) {
384         if (!(element instanceof DataObject)) {
385             throw new IllegalArgumentException("Only DataObjects can be stored in this list.");
386         }
387 
388         synchronized (elements) {
389             elements.add(index, element);
390         }
391     }
392 
393     /**
394      * @see java.util.Collection#add(Object)
395      */
396     public boolean add(Object o) {
397         if (!(o instanceof DataObject)) {
398             throw new IllegalArgumentException("Only DataObjects can be stored in this list.");
399         }
400 
401         synchronized (elements) {
402             return elements.add(o);
403         }
404     }
405 
406     /**
407      * @see java.util.Collection#addAll(Collection)
408      */
409     public boolean addAll(Collection c) {
410         synchronized (elements) {
411             return elements.addAll(c);
412         }
413     }
414 
415     /**
416      * @see java.util.List#addAll(int, Collection)
417      */
418     public boolean addAll(int index, Collection c) {
419         synchronized (elements) {
420             return elements.addAll(index, c);
421         }
422     }
423 
424     /**
425      * @see java.util.Collection#clear()
426      */
427     public void clear() {
428         synchronized (elements) {
429             elements.clear();
430         }
431     }
432 
433     /**
434      * @see java.util.Collection#contains(Object)
435      */
436     public boolean contains(Object o) {
437         synchronized (elements) {
438             return elements.contains(o);
439         }
440     }
441 
442     /**
443      * @see java.util.Collection#containsAll(Collection)
444      */
445     public boolean containsAll(Collection c) {
446         synchronized (elements) {
447             return elements.containsAll(c);
448         }
449     }
450 
451     /**
452      * @see java.util.List#get(int)
453      */
454     public Object get(int index) {
455         synchronized (elements) {
456             Object o = elements.get(index);
457 
458             if (o instanceof Map) {
459                 // read this page
460                 int pageStart = pageIndex(index) * pageSize;
461                 resolveInterval(pageStart, pageStart + pageSize);
462 
463                 return elements.get(index);
464             } else {
465                 return o;
466             }
467         }
468     }
469 
470     /**
471      * @see java.util.List#indexOf(Object)
472      */
473     public int indexOf(Object o) {
474         if (!(o instanceof DataObject)) {
475             return -1;
476         }
477 
478         DataObject dataObj = (DataObject) o;
479         if (dataObj.getDataContext() != dataContext) {
480             return -1;
481         }
482 
483         if (!dataObj
484             .getObjectId()
485             .getObjClass()
486             .getName()
487             .equals(rootEntity.getClassName())) {
488             return -1;
489         }
490 
491         Map idMap = dataObj.getObjectId().getIdSnapshot();
492 
493         synchronized (elements) {
494             for (int i = 0; i < elements.size(); i++) {
495                 // objects are in the same context, 
496                 // just comparing ids should be enough
497                 Object obj = elements.get(i);
498                 if (obj == dataObj) {
499                     return i;
500                 }
501 
502                 Map otherIdMap =
503                     (obj instanceof DataObject)
504                         ? ((DataObject) obj).getObjectId().getIdSnapshot()
505                         : (Map) obj;
506                 if (idMap.equals(otherIdMap)) {
507                     return i;
508                 }
509             }
510         }
511         return -1;
512     }
513 
514     /**
515      * @see java.util.Collection#isEmpty()
516      */
517     public boolean isEmpty() {
518         synchronized (elements) {
519             return elements.isEmpty();
520         }
521     }
522 
523     /**
524      * @see java.util.List#lastIndexOf(Object)
525      */
526     public int lastIndexOf(Object o) {
527         if (!(o instanceof DataObject)) {
528             return -1;
529         }
530 
531         DataObject dataObj = (DataObject) o;
532         if (dataObj.getDataContext() != dataContext) {
533             return -1;
534         }
535 
536         if (!dataObj
537             .getObjectId()
538             .getObjClass()
539             .getName()
540             .equals(rootEntity.getClassName())) {
541             return -1;
542         }
543 
544         Map idMap = dataObj.getObjectId().getIdSnapshot();
545 
546         synchronized (elements) {
547             for (int i = elements.size() - 1; i <= 0; i--) {
548                 // objects are in the same context, 
549                 // just comparing ids should be enough
550                 Object obj = elements.get(i);
551                 if (obj == dataObj) {
552                     return i;
553                 }
554 
555                 Map otherIdMap =
556                     (obj instanceof DataObject)
557                         ? ((DataObject) obj).getObjectId().getIdSnapshot()
558                         : (Map) obj;
559                 if (idMap.equals(otherIdMap)) {
560                     return i;
561                 }
562             }
563         }
564         return -1;
565     }
566 
567     /**
568      * @see java.util.List#remove(int)
569      */
570     public Object remove(int index) {
571         synchronized (elements) {
572             return elements.remove(index);
573         }
574     }
575 
576     /**
577      * @see java.util.Collection#remove(Object)
578      */
579     public boolean remove(Object o) {
580         synchronized (elements) {
581             return elements.remove(o);
582         }
583     }
584 
585     /**
586      * @see java.util.Collection#removeAll(Collection)
587      */
588     public boolean removeAll(Collection c) {
589         synchronized (elements) {
590             return elements.removeAll(c);
591         }
592     }
593 
594     /**
595      * @see java.util.Collection#retainAll(Collection)
596      */
597     public boolean retainAll(Collection c) {
598         synchronized (elements) {
599             return elements.retainAll(c);
600         }
601     }
602 
603     /**
604      * @see java.util.List#set(int, Object)
605      */
606     public Object set(int index, Object element) {
607         if (!(element instanceof DataObject)) {
608             throw new IllegalArgumentException("Only DataObjects can be stored in this list.");
609         }
610 
611         synchronized (elements) {
612             return elements.set(index, element);
613         }
614     }
615 
616     /**
617      * @see java.util.Collection#size()
618      */
619     public int size() {
620         synchronized (elements) {
621             return elements.size();
622         }
623     }
624 
625     public List subList(int fromIndex, int toIndex) {
626         synchronized (elements) {
627             resolveInterval(fromIndex, toIndex);
628             return elements.subList(fromIndex, toIndex);
629         }
630     }
631 
632     public Object[] toArray() {
633         resolveAll();
634 
635         return elements.toArray();
636     }
637 
638     /**
639      * @see java.util.Collection#toArray(Object[])
640      */
641     public Object[] toArray(Object[] a) {
642         resolveAll();
643 
644         return elements.toArray(a);
645     }
646 
647     /**
648      * Returns a total number of objects that are not resolved yet.
649      */
650     public int getUnfetchedObjects() {
651         return unfetchedObjects;
652     }
653 }