001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo;
012:
013: import com.versant.core.common.Debug;
014: import com.versant.core.common.QueryResultContainer;
015: import com.versant.core.common.Stack;
016: import com.versant.core.server.CompiledQuery;
017: import com.versant.core.server.QueryResultWrapper;
018:
019: import com.versant.core.common.BindingSupportImpl;
020: import com.versant.core.storagemanager.ExecuteQueryReturn;
021:
022: /**
023: * Forward only Iterator for query results. Data is fetched lazily in batches
024: * as it is required. Instances of this class are created when iterator()
025: * is called on the results of a JDOQL query. This supports multithreaded
026: * PM access by synchronizing some operations on the PM proxy.
027: *
028: * @see com.versant.core.jdo.ForwardQueryResult
029: * @see RandomAccessQueryResult
030: * @see QueryDetails#setResultBatchSize
031: */
032: public final class QueryResultIterator implements JDOListIterator {
033:
034: private PMProxy pmProxy;
035: /**
036: * Indicates that the iterator is closed.
037: */
038: private boolean closed = false;
039: /**
040: * Indicates that all results from the jdbc query has been processed.
041: */
042: private boolean queryFinished = false;
043: /**
044: * This is used as a token to identify the serverside queryResult.
045: */
046: private QueryResultWrapper qrsIF = null;
047: /**
048: * A stack where all retrieved results is kept till it is iterated past.
049: */
050: public final Stack stack = new Stack();
051:
052: public int toSkip;
053: private int nextIndex;
054:
055: public QueryResultIterator(PMProxy pmProxy,
056: QueryDetails queryDetails, CompiledQuery compiledQuery,
057: Object[] params, boolean doNotFlush) {
058: this .pmProxy = pmProxy;
059: // no need to synchronize on pmProxy here as we are created inside
060: // synchronized block
061: if (!doNotFlush && !queryDetails.isIgnoreCache()) {
062: pmProxy.flushIfDepOn(compiledQuery.getEvictionClassBits());
063: }
064: qrsIF = pmProxy.executeQuery(compiledQuery, params);
065: }
066:
067: public QueryResultIterator(PMProxy pmProxy,
068: QueryResultWrapper qrsw, QueryResultContainer container,
069: int index) {
070: this .pmProxy = pmProxy;
071: qrsIF = qrsw;
072: nextIndex = index;
073: if (container != null)
074: container.addToQueryStack(stack);
075: if (container != null && container.qFinished)
076: queryFinished = true;
077: }
078:
079: public int nextIndex() {
080: return nextIndex;
081: }
082:
083: public boolean hasNext() {
084: if (closed)
085: return false;
086: if (stack.size() > 0)
087: return true;
088: addToQueryStack();
089: return (stack.size() > 0);
090: }
091:
092: /**
093: * Try and add more results to the query stack. This also triggers
094: * processing of the ReferenceQuery for the PMs local cache. This stops
095: * long running reporting type queries from leaking SoftReferences.
096: */
097: private void addToQueryStack() {
098: final VersantPersistenceManagerImp realPM = pmProxy.getRealPM();
099: synchronized (pmProxy) {
100: realPM.processLocalCacheReferenceQueue();
101: QueryResultContainer container = null;
102: if (!queryFinished && stack.isEmpty()) {
103: container = realPM.getStorageManager()
104: .fetchNextQueryResult(
105: realPM,
106: ((ExecuteQueryReturn) qrsIF)
107: .getRunningQuery(), toSkip);
108: toSkip = 0;
109:
110: if (container == null) {
111: closed = true;
112: return;
113: }
114:
115: if (container.qFinished)
116: queryFinished = true;
117: if (Debug.DEBUG) {
118: Debug.OUT.println("############ amount recieved = "
119: + container.size() + " for queryId = "
120: + qrsIF + " (inner container size "
121: + container.container.size() + ")");
122: }
123: //add to managed cache.
124: realPM.addToCache(container.container);
125: //add the results to the stack.
126: container.addToQueryStack(stack);
127: }
128: if (container != null && container.qFinished)
129: queryFinished = true;
130: if (container != null)
131: container.reset();
132: }
133: }
134:
135: public Object next() {
136: if (!hasNext())
137: throw BindingSupportImpl.getInstance().noSuchElement("");
138: nextIndex++;
139: return QueryResultBase.resolveRow(stack.pop(), pmProxy);
140: }
141:
142: public final void close() {
143: if (closed)
144: return;
145: try {
146: stack.close();
147: if (qrsIF != null) {
148: synchronized (pmProxy) {
149: pmProxy.getRealPM().getStorageManager().closeQuery(
150: ((ExecuteQueryReturn) qrsIF)
151: .getRunningQuery());
152: }
153: qrsIF = null;
154: }
155: } catch (Exception e) {
156: throw BindingSupportImpl.getInstance().internal(
157: e.getMessage(), e);
158: } finally {
159: closed = true;
160: }
161: }
162:
163: public final void remove() {
164: throw BindingSupportImpl.getInstance().unsupported(
165: "Not allowed to modify");
166: }
167:
168: public boolean hasPrevious() {
169: throw BindingSupportImpl.getInstance().unsupportedOperation(
170: null);
171: }
172:
173: public Object previous() {
174: throw BindingSupportImpl.getInstance().unsupportedOperation(
175: null);
176: }
177:
178: public int previousIndex() {
179: throw BindingSupportImpl.getInstance().unsupportedOperation(
180: null);
181: }
182:
183: public void set(Object o) {
184: throw BindingSupportImpl.getInstance().unsupportedOperation(
185: null);
186: }
187:
188: public void add(Object o) {
189: throw BindingSupportImpl.getInstance().unsupportedOperation(
190: null);
191: }
192: }
|