/**
* Speedo: an implementation of JDO compliant personality on top of JORM generic
* I/O sub-system.
* Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*
* Contact: speedo@objectweb.org
*
* Authors: S.Chassande-Barrioz.
*
*/
package org.objectweb.speedo.query.jdo;
import org.objectweb.jorm.api.PClassMapping;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.api.PMapper;
import org.objectweb.jorm.naming.api.PName;
import org.objectweb.speedo.mapper.api.JormFactory;
import org.objectweb.speedo.mim.api.HomeItf;
import org.objectweb.speedo.pm.api.POManagerItf;
import org.objectweb.speedo.pm.jdo.api.JDOPOManagerItf;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import javax.jdo.Extent;
import javax.jdo.FetchPlan;
import javax.jdo.JDOException;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
*/
public class JDOExtent implements Extent {
private Class candidateClass = null;
private boolean hasSubclasses = false;
private boolean prefetch = false;
private JDOPOManagerItf pm = null;
private PClassMapping pcm;
private Logger logger;
private List iterators;
private int nextUnused;
/**
* The fetch plan.
* When an Extent is retrieved from a PersistenceManager, its FetchPlan is initialized to the same settings
* as that of the PersistenceManager.
* Subsequent modifications of the Extent's FetchPlan are not reflected
* in the FetchPlan of the PersistenceManager.
*/
private FetchPlan fetchPlan;
/**
* create a new SpeedoExtent object, this object is not obtained by a
* call to 'new' by the client application, but by the PersistenceManager
* object with getExtent() method
* @param candidateClass
* @param hasSubclasses
* @param pm
*/
public JDOExtent(Class candidateClass,
boolean hasSubclasses,
JDOPOManagerItf pm,
JormFactory jf,
boolean prefetch,
Logger logger) {
iterators = new ArrayList();
nextUnused = 0;
this.candidateClass = candidateClass;
this.hasSubclasses = hasSubclasses;
this.prefetch = prefetch;
this.logger = logger;
this.pm = pm;
//initialize the fetch plan to the same settings as that of the peristence manager
this.fetchPlan = pm.getFetchPlan();
try {
pcm = jf.getPClassMapping(candidateClass);
} catch (PException e) {
throw new JDOUserException("The class '" + candidateClass
+ "' is not mapped or is not a persistent class",
new Exception[]{e});
}
if (pcm == null) {
throw new JDOUserException("The class '" + candidateClass
+ "' is not mapped or is not a persistent class");
}
if (prefetch) {
prefetch = ((HomeItf) pcm).getPrefetchOnExtent();
}
}
/**
* Returns an iterator over all the instances in the Extent.
* @return an iterator over all instances in the Extent
*/
public synchronized Iterator iterator() {
POIterator poi;
int size = iterators.size();
if (nextUnused < size) {
// free POIterator instances availlable
poi = (POIterator) iterators.get(nextUnused);
} else {
// Allocate a POIterator
poi = new POIterator();
iterators.add(nextUnused, poi);
}
nextUnused++;
try {
poi.open(pm, pcm, hasSubclasses, prefetch);
} catch (PException e) {
throw new JDOException("Problem to fetch an Iterator over the class "
+ candidateClass.getName(), new Exception[]{e});
}
return poi;
}
/**
* Returns whether this Extent was defined to contain subclasses.
* @return true if this Extent was defined to contain instances
* that are of a subclass type
*/
public boolean hasSubclasses() {
return hasSubclasses;
}
/**
* An Extent contains all instances of a particular Class in the data
* store; this method returns the Class of the instances
* @return the Class of instances of this Extent
*/
public Class getCandidateClass() {
return candidateClass;
}
/**
* An Extent is managed by a PersistenceManager; this method gives access
* to the owning PersistenceManager.
* @return the owning PersistenceManager
*/
public PersistenceManager getPersistenceManager() {
return pm;
}
public FetchPlan getFetchPlan(){
return fetchPlan;
}
public void setFetchPlan(FetchPlan fp){
fetchPlan = fp;
}
/**
* Close all Iterators associated with this Extent instance. Iterators closed
* by this method will return false to hasNext() and will throw
* NoSuchElementException on next(). The Extent instance can still be used
* as a parameter of Query.setExtent, and to get an Iterator.
*/
public synchronized void closeAll() {
if (iterators.size() == 0) {
return;
}
for(int i=0; i<nextUnused; i++) {
POIterator poi = (POIterator) iterators.get(i);
try {
poi.close();
} catch (PException e) {
logger.log(BasicLevel.ERROR,
"Impossible to close an Iterator over the class "
+ candidateClass.getName(), e);
}
}
nextUnused = 0;
}
/**
* Close an Iterator associated with this Extent instance. Iterators closed
* by this method will return false to hasNext() and will throw
* NoSuchElementException on next(). The Extent instance can still be used
* as a parameter of Query.setExtent, and to get an Iterator.
* @param it an iterator obtained by the method iterator() on this Extent instance.
*/
public synchronized void close(Iterator it) {
if (it instanceof POIterator) {
try {
((POIterator) it).close();
} catch (PException e) {
logger.log(BasicLevel.ERROR,
"Impossible to close an Iterator over the class "
+ candidateClass.getName(), e);
}
int idx = iterators.indexOf(it);
if (idx == -1) {
logger.log(BasicLevel.WARN,
"The Iterator not managed by this Extent has been closed");
} else if (idx < nextUnused) {
iterators.add(iterators.remove(idx));
nextUnused--;
} else {
//nothing to do the iterator is already unused
}
}
}
}
class POIterator implements Iterator {
private JDOPOManagerItf pm;
private Iterator pnameIt;
private boolean closed;
private boolean connectionOpened;
private PMapper mapper;
private Object connection;
public void open(JDOPOManagerItf pm,
PClassMapping pcm,
boolean subclass,
boolean prefetch) throws PException {
this.pm = pm;
closed = false;
this.mapper = pcm.getPMapper();
Object cs = pm.getConnectionSpec();
if (cs == null) {
connection = mapper.getConnection();
} else {
connection = mapper.getConnection(cs);
}
connectionOpened = true;
pnameIt = pcm.getPNameIterator(connection, subclass, prefetch, pm.currentTransaction());
}
public void close() throws PException {
closed = true;
if (connectionOpened) {
connectionOpened = false;
mapper.closeConnection(connection);
}
pm = null;
mapper = null;
connection = null;
pnameIt = null;
}
public boolean hasNext() {
if (closed || pm.isPOMClosed()) {
throw new NoSuchElementException();
}
if (connectionOpened && !pnameIt.hasNext()) {
connectionOpened = false;
try {
mapper.closeConnection(connection);
} catch (PException e) {
}
}
return pnameIt.hasNext();
}
public Object next() {
if (closed || pm.isPOMClosed() || !pnameIt.hasNext()) {
if (connectionOpened) {
connectionOpened = false;
try {
mapper.closeConnection(connection);
} catch (PException e) {
}
}
throw new NoSuchElementException();
}
PName pn = (PName) pnameIt.next();
return pm.getObjectById(pn, false);
}
public void remove() {
throw new UnsupportedOperationException();
}
protected void finalize() throws Throwable {
super.finalize();
if (connectionOpened && mapper!=null) {
connectionOpened = false;
try {
mapper.closeConnection(connection);
} catch (PException e) {
}
}
}
}