001: /**
002: * Speedo: an implementation of JDO compliant personality on top of JORM generic
003: * I/O sub-system.
004: * Copyright (C) 2001-2004 France Telecom R&D
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: *
021: *
022: * Contact: speedo@objectweb.org
023: *
024: * Authors: S.Chassande-Barrioz.
025: *
026: */package org.objectweb.speedo.query.jdo;
027:
028: import org.objectweb.jorm.api.PClassMapping;
029: import org.objectweb.jorm.api.PException;
030: import org.objectweb.jorm.api.PMapper;
031: import org.objectweb.jorm.naming.api.PName;
032: import org.objectweb.speedo.mapper.api.JormFactory;
033: import org.objectweb.speedo.mim.api.HomeItf;
034: import org.objectweb.speedo.pm.api.POManagerItf;
035: import org.objectweb.speedo.pm.jdo.api.JDOPOManagerItf;
036: import org.objectweb.util.monolog.api.BasicLevel;
037: import org.objectweb.util.monolog.api.Logger;
038:
039: import javax.jdo.Extent;
040: import javax.jdo.FetchPlan;
041: import javax.jdo.JDOException;
042: import javax.jdo.JDOUserException;
043: import javax.jdo.PersistenceManager;
044: import java.util.ArrayList;
045: import java.util.Iterator;
046: import java.util.List;
047: import java.util.NoSuchElementException;
048:
049: /**
050: */
051: public class JDOExtent implements Extent {
052:
053: private Class candidateClass = null;
054: private boolean hasSubclasses = false;
055: private boolean prefetch = false;
056: private JDOPOManagerItf pm = null;
057: private PClassMapping pcm;
058: private Logger logger;
059: private List iterators;
060: private int nextUnused;
061:
062: /**
063: * The fetch plan.
064: * When an Extent is retrieved from a PersistenceManager, its FetchPlan is initialized to the same settings
065: * as that of the PersistenceManager.
066: * Subsequent modifications of the Extent's FetchPlan are not reflected
067: * in the FetchPlan of the PersistenceManager.
068: */
069: private FetchPlan fetchPlan;
070:
071: /**
072: * create a new SpeedoExtent object, this object is not obtained by a
073: * call to 'new' by the client application, but by the PersistenceManager
074: * object with getExtent() method
075: * @param candidateClass
076: * @param hasSubclasses
077: * @param pm
078: */
079: public JDOExtent(Class candidateClass, boolean hasSubclasses,
080: JDOPOManagerItf pm, JormFactory jf, boolean prefetch,
081: Logger logger) {
082: iterators = new ArrayList();
083: nextUnused = 0;
084: this .candidateClass = candidateClass;
085: this .hasSubclasses = hasSubclasses;
086: this .prefetch = prefetch;
087: this .logger = logger;
088: this .pm = pm;
089: //initialize the fetch plan to the same settings as that of the peristence manager
090: this .fetchPlan = pm.getFetchPlan();
091: try {
092: pcm = jf.getPClassMapping(candidateClass);
093: } catch (PException e) {
094: throw new JDOUserException("The class '" + candidateClass
095: + "' is not mapped or is not a persistent class",
096: new Exception[] { e });
097: }
098: if (pcm == null) {
099: throw new JDOUserException("The class '" + candidateClass
100: + "' is not mapped or is not a persistent class");
101: }
102: if (prefetch) {
103: prefetch = ((HomeItf) pcm).getPrefetchOnExtent();
104: }
105: }
106:
107: /**
108: * Returns an iterator over all the instances in the Extent.
109: * @return an iterator over all instances in the Extent
110: */
111: public synchronized Iterator iterator() {
112: POIterator poi;
113: int size = iterators.size();
114: if (nextUnused < size) {
115: // free POIterator instances availlable
116: poi = (POIterator) iterators.get(nextUnused);
117: } else {
118: // Allocate a POIterator
119: poi = new POIterator();
120: iterators.add(nextUnused, poi);
121: }
122: nextUnused++;
123: try {
124: poi.open(pm, pcm, hasSubclasses, prefetch);
125: } catch (PException e) {
126: throw new JDOException(
127: "Problem to fetch an Iterator over the class "
128: + candidateClass.getName(),
129: new Exception[] { e });
130: }
131: return poi;
132: }
133:
134: /**
135: * Returns whether this Extent was defined to contain subclasses.
136: * @return true if this Extent was defined to contain instances
137: * that are of a subclass type
138: */
139: public boolean hasSubclasses() {
140: return hasSubclasses;
141: }
142:
143: /**
144: * An Extent contains all instances of a particular Class in the data
145: * store; this method returns the Class of the instances
146: * @return the Class of instances of this Extent
147: */
148: public Class getCandidateClass() {
149: return candidateClass;
150: }
151:
152: /**
153: * An Extent is managed by a PersistenceManager; this method gives access
154: * to the owning PersistenceManager.
155: * @return the owning PersistenceManager
156: */
157: public PersistenceManager getPersistenceManager() {
158: return pm;
159: }
160:
161: public FetchPlan getFetchPlan() {
162: return fetchPlan;
163: }
164:
165: public void setFetchPlan(FetchPlan fp) {
166: fetchPlan = fp;
167: }
168:
169: /**
170: * Close all Iterators associated with this Extent instance. Iterators closed
171: * by this method will return false to hasNext() and will throw
172: * NoSuchElementException on next(). The Extent instance can still be used
173: * as a parameter of Query.setExtent, and to get an Iterator.
174: */
175: public synchronized void closeAll() {
176: if (iterators.size() == 0) {
177: return;
178: }
179: for (int i = 0; i < nextUnused; i++) {
180: POIterator poi = (POIterator) iterators.get(i);
181: try {
182: poi.close();
183: } catch (PException e) {
184: logger.log(BasicLevel.ERROR,
185: "Impossible to close an Iterator over the class "
186: + candidateClass.getName(), e);
187: }
188: }
189: nextUnused = 0;
190: }
191:
192: /**
193: * Close an Iterator associated with this Extent instance. Iterators closed
194: * by this method will return false to hasNext() and will throw
195: * NoSuchElementException on next(). The Extent instance can still be used
196: * as a parameter of Query.setExtent, and to get an Iterator.
197: * @param it an iterator obtained by the method iterator() on this Extent instance.
198: */
199: public synchronized void close(Iterator it) {
200: if (it instanceof POIterator) {
201: try {
202: ((POIterator) it).close();
203: } catch (PException e) {
204: logger.log(BasicLevel.ERROR,
205: "Impossible to close an Iterator over the class "
206: + candidateClass.getName(), e);
207: }
208: int idx = iterators.indexOf(it);
209: if (idx == -1) {
210: logger
211: .log(BasicLevel.WARN,
212: "The Iterator not managed by this Extent has been closed");
213: } else if (idx < nextUnused) {
214: iterators.add(iterators.remove(idx));
215: nextUnused--;
216: } else {
217: //nothing to do the iterator is already unused
218: }
219: }
220: }
221: }
222:
223: class POIterator implements Iterator {
224:
225: private JDOPOManagerItf pm;
226: private Iterator pnameIt;
227: private boolean closed;
228: private boolean connectionOpened;
229: private PMapper mapper;
230: private Object connection;
231:
232: public void open(JDOPOManagerItf pm, PClassMapping pcm,
233: boolean subclass, boolean prefetch) throws PException {
234: this .pm = pm;
235: closed = false;
236: this .mapper = pcm.getPMapper();
237: Object cs = pm.getConnectionSpec();
238: if (cs == null) {
239: connection = mapper.getConnection();
240: } else {
241: connection = mapper.getConnection(cs);
242: }
243: connectionOpened = true;
244: pnameIt = pcm.getPNameIterator(connection, subclass, prefetch,
245: pm.currentTransaction());
246: }
247:
248: public void close() throws PException {
249: closed = true;
250: if (connectionOpened) {
251: connectionOpened = false;
252: mapper.closeConnection(connection);
253: }
254: pm = null;
255: mapper = null;
256: connection = null;
257: pnameIt = null;
258: }
259:
260: public boolean hasNext() {
261: if (closed || pm.isPOMClosed()) {
262: throw new NoSuchElementException();
263: }
264: if (connectionOpened && !pnameIt.hasNext()) {
265: connectionOpened = false;
266: try {
267: mapper.closeConnection(connection);
268: } catch (PException e) {
269: }
270: }
271: return pnameIt.hasNext();
272: }
273:
274: public Object next() {
275: if (closed || pm.isPOMClosed() || !pnameIt.hasNext()) {
276: if (connectionOpened) {
277: connectionOpened = false;
278: try {
279: mapper.closeConnection(connection);
280: } catch (PException e) {
281: }
282: }
283: throw new NoSuchElementException();
284: }
285: PName pn = (PName) pnameIt.next();
286: return pm.getObjectById(pn, false);
287: }
288:
289: public void remove() {
290: throw new UnsupportedOperationException();
291: }
292:
293: protected void finalize() throws Throwable {
294: super .finalize();
295: if (connectionOpened && mapper != null) {
296: connectionOpened = false;
297: try {
298: mapper.closeConnection(connection);
299: } catch (PException e) {
300: }
301: }
302: }
303: }
|