Index: topia/src/java/org/codelutin/topia/persistence/jdo/JDOEntity.java diff -u topia/src/java/org/codelutin/topia/persistence/jdo/JDOEntity.java:1.1 topia/src/java/org/codelutin/topia/persistence/jdo/JDOEntity.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/jdo/JDOEntity.java:1.1 Thu Jul 15 13:13:13 2004 +++ topia/src/java/org/codelutin/topia/persistence/jdo/JDOEntity.java Wed Sep 15 14:36:04 2004 @@ -23,19 +23,22 @@ * * @author Benjamin Poussin * Copyright Code Lutin - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Mise a jour: $Date: 2004/07/15 13:13:13 $ - * par : $Author: bpoussin $ + * Mise a jour: $Date: 2004/09/15 14:36:04 $ + * par : $Author: pineau $ */ package org.codelutin.topia.persistence.jdo; +import java.util.Date; +import java.util.HashSet; + +import javax.jdo.PersistenceManager; + import org.codelutin.topia.TopiaEntity; -import org.codelutin.topia.TopiaUser; import org.codelutin.topia.TopiaException; -import java.util.HashSet; -import java.util.Date; +import org.codelutin.topia.TopiaUser; public interface JDOEntity { // JDOEntity public Class getEntityClass(); @@ -52,7 +55,7 @@ public void set_lastUpdateDate_(Date v); public void set_lastUpdateUser_(TopiaUser v); - public void update(TopiaEntity topiaEntity, HashSet done) throws TopiaException; + public void update(TopiaEntity topiaEntity, HashSet done, PersistenceManager pm) throws TopiaException; public TopiaEntity convertToEntity() throws TopiaException; } // JDOEntity Index: topia/src/java/org/codelutin/topia/persistence/jdo/JDOPersistenceHelper.java diff -u topia/src/java/org/codelutin/topia/persistence/jdo/JDOPersistenceHelper.java:1.6 topia/src/java/org/codelutin/topia/persistence/jdo/JDOPersistenceHelper.java:1.7 --- topia/src/java/org/codelutin/topia/persistence/jdo/JDOPersistenceHelper.java:1.6 Fri Aug 6 18:02:38 2004 +++ topia/src/java/org/codelutin/topia/persistence/jdo/JDOPersistenceHelper.java Wed Sep 15 14:36:04 2004 @@ -16,17 +16,16 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *##%*/ -/* * +/******************************************************************************* * JDOPersistenceHelper.java - * + * * Created: 6 juil. 2004 - * - * @author Benjamin Poussin - * Copyright Code Lutin - * @version $Revision: 1.6 $ - * - * Mise a jour: $Date: 2004/08/06 18:02:38 $ - * par : $Author: bpoussin $ + * + * @author Benjamin Poussin Copyright Code Lutin + * + * @version $Revision: 1.7 $ + * + * Mise a jour: $Date: 2004/09/15 14:36:04 $ par : $Author: pineau $ */ package org.codelutin.topia.persistence.jdo; @@ -50,6 +49,7 @@ import org.codelutin.queryjdo.JDOQueryHelper; import org.codelutin.queryparser.QueryHelper; import org.codelutin.queryparser.QueryHelperException; +import org.codelutin.topia.AsynchronousLoader; import org.codelutin.topia.TopiaContext; import org.codelutin.topia.TopiaEntity; import org.codelutin.topia.TopiaException; @@ -60,68 +60,90 @@ public class JDOPersistenceHelper extends AbstractPersistenceHelper { // JDOPersistenceHelper - /** - * Le AbstractPersistenceHelper JDO - */ - protected PersistenceManager pm = null; - /** - * Le queryHelper qui permet d'executer les requetes TopiaQuery - */ - protected QueryHelper queryHelper = null; + protected PersistenceManagerFactory pmf = null; - public JDOPersistenceHelper(TopiaContext context, Properties properties){ + public JDOPersistenceHelper(TopiaContext context, Properties properties) { super(context, properties); - PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(getProperties()); - pm = pmf.getPersistenceManager(); - pm.setUserObject(this); + + // Override jar settings with systems properties if they are set + String connectionDriverName = System + .getProperty("javax.jdo.option.ConnectionDriverName"); + if (connectionDriverName != null) { + getProperties().setProperty( + "javax.jdo.option.ConnectionDriverName", + connectionDriverName); + } + String connectionURL = System + .getProperty("javax.jdo.option.ConnectionURL"); + if (connectionURL != null) { + getProperties().setProperty("javax.jdo.option.ConnectionURL", + connectionURL); + } + String connectionUserName = System + .getProperty("javax.jdo.option.ConnectionUserName"); + if (connectionUserName != null) { + getProperties().setProperty("javax.jdo.option.ConnectionUserName", + connectionUserName); + } + String connectionPassword = System + .getProperty("javax.jdo.option.ConnectionPassword"); + if (connectionPassword != null) { + getProperties().setProperty("javax.jdo.option.ConnectionPassword", + connectionPassword); + } + + pmf = JDOHelper.getPersistenceManagerFactory(getProperties()); } - protected boolean isNewObject(TopiaEntity entity) throws TopiaException { + protected boolean isNewObject(TopiaEntity entity) throws TopiaException { String objectTopiaId = entity.get_topiaId_(); String objectVersion = entity.get_version_(); - return objectTopiaId == null || objectVersion == null || "".equals(objectVersion) || VersionNumberUtil.equals("0", objectVersion); + return objectTopiaId == null || objectVersion == null + || "".equals(objectVersion) + || VersionNumberUtil.equals("0", objectVersion); } /** - * Permet de convertir les objets normaux en objet persistent. - * La mise à jour des inforamtions du framework est faite ici - * (lastModificationDate, ...) - */ - public JDOEntity update(TopiaEntity entity, HashSet done) throws TopiaException { + * Permet de convertir les objets normaux en objet persistent. La mise à + * jour des inforamtions du framework est faite ici (lastModificationDate, + * ...) + */ + public JDOEntity update(TopiaEntity entity, HashSet done, + PersistenceManager pm) throws TopiaException { JDOEntity jdoEntity = null; String objectTopiaId = entity.get_topiaId_(); - try{ + try { if (!isNewObject(entity)) { - jdoEntity = findDO(objectTopiaId); + jdoEntity = findDO(objectTopiaId, pm); } else { Class jdoClass = Util.getJDOClass(entity.getEntityClass()); - jdoEntity = (JDOEntity)Util.getInstance(jdoClass); + jdoEntity = (JDOEntity) Util.getInstance(jdoClass); jdoEntity.set_topiaId_(entity.get_topiaId_()); pm.makePersistent(jdoEntity); } - if(entity instanceof AbstractLazyEntity){ + if (entity instanceof AbstractLazyEntity) { AbstractLazyEntity ale = (AbstractLazyEntity) entity; - if(!ale.isLoaded()) { + if (!ale.isLoaded()) { // if is not loaded, then no modification done return jdoEntity; } entity = ale.getEntity(); } - - if(!done.contains(entity.get_topiaId_())){ + if (!done.contains(entity.get_topiaId_())) { // si on ne la pas encore convertie on le fait done.add(entity.get_topiaId_()); - //FIXME mettre a jour les info lastModificationDate, ... sur entity + //FIXME mettre a jour les info lastModificationDate, ... sur + // entity String version = entity.get_version_(); version = VersionNumberUtil.inc(version); entity.set_version_(version); - jdoEntity.update(entity, done); + jdoEntity.update(entity, done, pm); } } catch (Throwable eee) { - throw (new TopiaException("Cannot make persistent !", eee)); + throw (new TopiaException("Cannot create persistent state !", eee)); } return jdoEntity; @@ -130,149 +152,280 @@ public TopiaEntity makePersistent(TopiaEntity entity) throws TopiaException { if (!isNewObject(entity)) { - throw new TopiaException("Can't persist " + entity + " has it seems to exist, please use update instead !"); + throw new TopiaException("Can't persist " + entity + + " has it seems to exist, please use update instead !"); } + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); Transaction transaction = pm.currentTransaction(); transaction.begin(); try { - JDOEntity jdoEntity = update(entity, new HashSet()); + JDOEntity jdoEntity = update(entity, new HashSet(), pm); entity = jdoEntity.convertToEntity(); // TODO ne pas mettre le listener ici // mais plutot dans le PersistenceService // car ce n'est pas specifique au JDO getContext().getListeners().addCategory(this, entity); transaction.commit(); - } catch (TopiaException eee) { + } catch (Exception eee) { transaction.rollback(); - throw eee; + throw new TopiaException("Can't persist entity " + entity, eee); } return entity; } - public TopiaEntity update(TopiaEntity entity) throws TopiaException{ + public TopiaEntity update(TopiaEntity entity) throws TopiaException { if (isNewObject(entity)) { - throw new TopiaException("Can't update " + entity + " has it does not seem to exist, please use makePersistent instead !"); + throw new TopiaException( + "Can't update " + + entity + + " has it does not seem to exist, please use makePersistent instead !"); } + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + Transaction transaction = pm.currentTransaction(); transaction.begin(); try { - JDOEntity jdoEntity = update(entity, new HashSet()); + JDOEntity jdoEntity = update(entity, new HashSet(), pm); entity = jdoEntity.convertToEntity(); getContext().getListeners().addCategory(this, entity); transaction.commit(); - } catch (TopiaException eee) { + } catch (Exception eee) { transaction.rollback(); - throw eee; + throw new TopiaException("Can't update entity " + entity, eee); } return entity; } - public void delete(TopiaEntity entity) throws TopiaException{ + public void delete(TopiaEntity entity) throws TopiaException { String id = entity.get_topiaId_(); if (isNewObject(entity)) { - throw new TopiaException("Can't delete " + entity + " has it seems not to exist (no identity found) !"); + throw new TopiaException("Can't delete " + entity + + " has it seems not to exist (no identity found) !"); } + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + Transaction transaction = pm.currentTransaction(); transaction.begin(); try { - pm.deletePersistent(findDO(id)); + pm.deletePersistent(findDO(id, pm)); transaction.commit(); - } catch (TopiaException eee) { + } catch (Exception eee) { transaction.rollback(); - throw (eee); + throw new TopiaException("Can't delete entity " + entity, eee); } } /** - * Permet de récupéré une liste d'objet par rapport a une requete. - * @param query la requete a executer - * @return une List d'objets - */ - public List find(TopiaQuery query) throws TopiaException{ + * Get a list of objects within a range for the given query. + * + * @param query + * request to execute + * @param startIndex + * index of the first element to return + * @param endIndex + * index of the last element to return + * @return a list og Objects + */ + public List findInRange(TopiaQuery query, int startIndex, int endIndex) + throws TopiaException { + if (0 > startIndex || endIndex < startIndex) { + throw new TopiaException("Illegal range ([" + startIndex + "," + + endIndex + "])"); + } + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + + Transaction transaction = pm.currentTransaction(); + transaction.begin(); + List result = new ArrayList(); - for(Iterator i=execute(query).iterator(); i.hasNext();){ - JDOEntity jdoEntity = (JDOEntity)i.next(); + Collection qc = execute(query, pm); + Object[] jdoObjects = qc.toArray(); + + for (int i = startIndex; i <= endIndex; i++) { + if (i >= jdoObjects.length) + break; // Not enough rows, return what we got so far. + JDOEntity jdoEntity = (JDOEntity) jdoObjects[i]; TopiaEntity entity = jdoEntity.convertToEntity(); result.add(entity); } + transaction.rollback(); + return result; } - public int size(TopiaQuery query) throws TopiaException{ - // TODO dans le futur faire un requete qui retourne la taille - // directement - return find(query).size(); - } + /** + * Get an asynchronous loader of objects within a range for the given query. + * + * @param query + * request to execute + * @param startIndex + * index of the first element to return + * @param endIndex + * index of the last element to return + * @return a JDOAsynchronousLoader + */ + public AsynchronousLoader findInRangeAsynchronously(TopiaQuery query, + int startIndex, int endIndex) throws TopiaException { + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); - public TopiaEntity findByTopiaId(String id) throws TopiaException{ - TopiaEntity entity = findDO(id).convertToEntity(); - return entity; + AsynchronousLoader loader = new JDOAsynchronousLoader(this, pm, query, + startIndex, endIndex); + Thread t = new Thread(loader); + t.setPriority(Thread.MIN_PRIORITY); + System.out.println("Prority is " + t.getPriority()); // TODO remove this + t.start(); + return loader; } -// /** -// * Recherche tous les objets correspondants au Id, il faut absolument -// * que les id référence tous le meme type d'objet exactement. -// */ -// public List findByTopiaId(List ids) throws TopiaException{ -// return null; -// } + /** + * Permet de récupéré une liste d'objet par rapport a une requete. + * + * @param query + * la requete a executer + * @return une List d'objets + */ + public List find(TopiaQuery query) throws TopiaException { + List result = new ArrayList(); + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); - protected QueryHelper getQueryHelper(){ - if(queryHelper == null){ - queryHelper = new JDOQueryHelper(pm); + Transaction transaction = pm.currentTransaction(); + transaction.begin(); + Collection qc = execute(query, pm); + for (Iterator i = qc.iterator(); i.hasNext();) { + JDOEntity jdoEntity = (JDOEntity) i.next(); + TopiaEntity entity = jdoEntity.convertToEntity(); + result.add(entity); } - return queryHelper; + transaction.rollback(); + return result; + } + + /** + * Permet de récupéré de manière asynchrone une liste d'objet par rapport a + * une requete. + * + * @param query + * la requete a executer + * @return a JDOAsynchronousLoader + */ + public AsynchronousLoader findAsynchronously(TopiaQuery query) + throws TopiaException { + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + + AsynchronousLoader loader = new JDOAsynchronousLoader(this, pm, query); + Thread t = new Thread(loader); + t.setPriority(Thread.MIN_PRIORITY); + System.out.println("Prority is " + t.getPriority()); // TODO remove this + t.start(); + return loader; + } + + public int size(TopiaQuery query) throws TopiaException { + // TODO dans le futur faire un requete qui retourne la taille + // directement + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + + Transaction transaction = pm.currentTransaction(); + transaction.begin(); + int size = execute(query, pm).size(); + transaction.rollback(); + return size; } + public TopiaEntity findByTopiaId(String id) throws TopiaException { + PersistenceManager pm = pmf.getPersistenceManager(); + pm.setUserObject(this); + Transaction transaction = pm.currentTransaction(); + transaction.begin(); + TopiaEntity entity = findDO(id, pm).convertToEntity(); + transaction.rollback(); + return entity; + } - protected Collection execute(TopiaQuery query) throws TopiaException{ - try{ - QueryHelper queryHelper = getQueryHelper(); + // /** + // * Recherche tous les objets correspondants au Id, il faut absolument + // * que les id référence tous le meme type d'objet exactement. + // */ + // public List findByTopiaId(List ids) throws TopiaException{ + // return null; + // } + + protected QueryHelper getQueryHelper(PersistenceManager pm) { + return new JDOQueryHelper(pm); + } + + // Petite visu package pour que le JDOAsynchronousLoader puisse y accéder + synchronized Collection execute(TopiaQuery query, PersistenceManager pm) + throws TopiaException { + try { + QueryHelper queryHelper = getQueryHelper(pm); // on change le type de class recherche String jdoClass = Util.getJDOClassName(query.getFrom()); query.from(jdoClass); // FIXME, this is hack while query don't support * in select - if("*".equals(query.getSelect())){ + if ("*".equals(query.getSelect())) { query.select(jdoClass); } - Logger.getLogger(getClass().getName() + ".execute").log(Level.INFO, "Executing query: " + query.getQueryString()); + Logger.getLogger(getClass().getName() + ".execute").log(Level.INFO, + "Executing query: " + query.getQueryString()); queryHelper.setQuery(query.getQueryString()); - queryHelper.setArgs(query.getArgs()); + + Collection args = query.getArgs(); + ArrayList processedArgs = new ArrayList(); + for (Iterator iter = args.iterator(); iter.hasNext();) { + Object arg = iter.next(); + if (arg instanceof TopiaEntity) { + arg = findDO(((TopiaEntity) arg).get_topiaId_(), pm); + } + processedArgs.add(arg); + } + queryHelper.setArgs(processedArgs); + return queryHelper.Helper(); - }catch(IOException eee){ + } catch (IOException eee) { throw new TopiaException("Error during query parsing", eee); - }catch(QueryHelperException eee){ + } catch (QueryHelperException eee) { throw new TopiaException("Error during query execution", eee); } } - public JDOEntity findDO(String topiaId) throws TopiaException{ + public JDOEntity findDO(String topiaId, PersistenceManager pm) + throws TopiaException { Class clazz = TopiaId.getClassName(topiaId); clazz = Util.getJDOClass(clazz); Query query = pm.newQuery(clazz, "_topiaId_ == topiaId"); query.declareParameters("java.lang.String topiaId"); - Collection queryResult = (Collection)query.execute(topiaId); + Collection queryResult = (Collection) query.execute(topiaId); JDOEntity result = null; int size = queryResult.size(); - if(size == 1){ - result = (JDOEntity)queryResult.iterator().next(); + if (size == 1) { + result = (JDOEntity) queryResult.iterator().next(); query.close(queryResult); - }else{ + } else { query.close(queryResult); - throw new TopiaException("Can't find objet or id is not unique (result size: " + size + ")"); + throw new TopiaException( + "Can't find objet or id is not unique (result size: " + + size + ")"); } return result; } - - public void importXML(String xml) throws TopiaException{ + public void importXML(String xml) throws TopiaException { // FIXME implanter l'import en XML } - public String exportXML() throws TopiaException{ + public String exportXML() throws TopiaException { // FIXME implanter l'export en XML return ""; } Index: topia/src/java/org/codelutin/topia/persistence/jdo/Util.java diff -u topia/src/java/org/codelutin/topia/persistence/jdo/Util.java:1.1 topia/src/java/org/codelutin/topia/persistence/jdo/Util.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/jdo/Util.java:1.1 Thu Jul 15 13:13:13 2004 +++ topia/src/java/org/codelutin/topia/persistence/jdo/Util.java Wed Sep 15 14:36:04 2004 @@ -23,10 +23,10 @@ * * @author Benjamin Poussin * Copyright Code Lutin - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Mise a jour: $Date: 2004/07/15 13:13:13 $ - * par : $Author: bpoussin $ + * Mise a jour: $Date: 2004/09/15 14:36:04 $ + * par : $Author: pineau $ */ package org.codelutin.topia.persistence.jdo; @@ -46,6 +46,10 @@ } public static String getLazyClassName(String clazz){ + // Don't process a String that allready point to a Lazy class + if (clazz.endsWith("Lazy")) { + return clazz; + } String pack = getPackageName(clazz); clazz = getClassName(clazz); @@ -53,6 +57,10 @@ } public static String getJDOClassName(String clazz){ + // Don't process a String that allready point to a JDO class + if (clazz.endsWith("JDO")) { + return clazz; + } String pack = getPackageName(clazz); clazz = getClassName(clazz); Index: topia/src/java/org/codelutin/topia/persistence/jdo/JDOAsynchronousLoader.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/jdo/JDOAsynchronousLoader.java:1.1 --- /dev/null Wed Sep 15 14:36:10 2004 +++ topia/src/java/org/codelutin/topia/persistence/jdo/JDOAsynchronousLoader.java Wed Sep 15 14:36:04 2004 @@ -0,0 +1,298 @@ +/* *##% + * Copyright (C) 2002, 2003, 2004 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * AsynchronousEntityLoader.java + * + * Created: Aug 30, 2004 + * + * @author Cédric Pineau + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2004/09/15 14:36:04 $ + * by : $Author: pineau $ + */ + +package org.codelutin.topia.persistence.jdo; + +import java.beans.Expression; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.jdo.PersistenceManager; +import javax.jdo.Transaction; + +import org.codelutin.generator.Util; +import org.codelutin.topia.AsynchronousLoader; +import org.codelutin.topia.AsynchronousLoaderEvent; +import org.codelutin.topia.AsynchronousLoaderListener; +import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaQuery; + +/** + * Classe creer par le persistent helper Prend la query a effectuer La lance au + * debut du run Propose à la fois des evenements de progression du traitements + * et des methodes standard + */ +public class JDOAsynchronousLoader implements AsynchronousLoader { + + protected final static int FETCHING_RANGE = 99; // that is 100 records + + protected final static int MAX_FETCHING_RANGE = 299; // that is 300 records + + // time + + protected TopiaQuery query = null; + + protected JDOPersistenceHelper helper; + + protected PersistenceManager pm; + + protected int startIndex = 0; + + protected int endIndex = 0; + + protected int dataSize = 0; + + protected HashSet listeners = new HashSet(); + + protected Object[] data = null; + + protected boolean urgentLoadingAsked = false; + + protected int urgentLoadingStartIndex = Integer.MAX_VALUE; + + protected int urgentLoadingEndIndex = 0; + + public JDOAsynchronousLoader(JDOPersistenceHelper helper, PersistenceManager pm, TopiaQuery query) { + this(helper, pm, query, 0, FETCHING_RANGE); + } + + public JDOAsynchronousLoader(JDOPersistenceHelper helper, PersistenceManager pm, TopiaQuery query, + int startIndex, int endIndex) { + this.helper = helper; + this.pm = pm; + this.query = query; + this.startIndex = startIndex; + this.endIndex = endIndex; + } + + public void addListener(AsynchronousLoaderListener listener) { + listeners.add(listener); + } + + public void removeListener(AsynchronousLoaderListener listener) { + listeners.remove(listener); + } + + public Set getListeners() { + return Collections.unmodifiableSet(listeners); + } + + public void fireDataLoaded(AsynchronousLoaderEvent event) { + for (Iterator iter = getListeners().iterator(); iter.hasNext();) { + AsynchronousLoaderListener listener = (AsynchronousLoaderListener) iter + .next(); + listener.dataLoaded(event); + } + } + + public void fetchDataInRange(Object[] jdoObjects, int startIndex, + int endIndex) throws TopiaException { + for (int i = startIndex; i <= endIndex; i++) { + // We may have loaded it allready (eg in an urgent request) + if (data[i] == null) { + JDOEntity jdoEntity = (JDOEntity) jdoObjects[i]; + TopiaEntity entity = jdoEntity.convertToEntity(); + data[i] = entity; + + // force loading of referenced entities + Field[] fields = entity.getEntityClass().getDeclaredFields(); + for (int j = 0; j < fields.length; j++) { + // Is it a topiaEntity ? + if (! TopiaEntity.class.isAssignableFrom(fields[j].getType())) continue; + // then load it + Expression e = new Expression(entity, "get" + + Util.toUpperCaseFirstLetter(fields[j].getName()), null); + try { + // load it ! + e.getValue(); + } catch (Exception eee) {} + } + + } + } + fireDataLoaded(new AsynchronousLoaderEvent(this, + AsynchronousLoaderEvent.OBJECTS_LOADED, startIndex, endIndex)); + } + + public void run() { + try { + Transaction transaction = pm.currentTransaction(); + transaction.begin(); + + // First get size of returned collection + Collection qc = helper.execute(query, pm); + dataSize = qc.size(); + fireDataLoaded(new AsynchronousLoaderEvent(this, + AsynchronousLoaderEvent.SIZE_IS_AVAILABLE)); + + data = new Object[dataSize]; + Object[] jdoObjects = qc.toArray(); + + boolean hasDataToFetch = (size() != 0); + boolean forward = false; + // fetch given range first + int currentStartIndex = startIndex; + int currentEndIndex = endIndex; + if (currentEndIndex >= size()) { + currentEndIndex = size() - 1; + } + + int currentBottomIndex = currentStartIndex; + int currentTopIndex = currentEndIndex; + + while (hasDataToFetch) { + + fetchDataInRange(jdoObjects, currentStartIndex, currentEndIndex); + + // fetch urgently requested indexes + if (urgentLoadingAsked) { + synchronized (this) { + int currentUrgentLoadingStartIndex; + int currentUrgentLoadingEndIndex; + if (urgentLoadingEndIndex - urgentLoadingStartIndex < MAX_FETCHING_RANGE) { + // Fetch from befor start to after end + currentUrgentLoadingStartIndex = Math.max(urgentLoadingStartIndex - FETCHING_RANGE, 0); + currentUrgentLoadingEndIndex = Math.min(urgentLoadingEndIndex + FETCHING_RANGE, size() - 1); + fetchDataInRange(jdoObjects, + currentUrgentLoadingStartIndex, + currentUrgentLoadingEndIndex); + } else { + // Range is too large. Must be kind of big table + // scroll or something + // so fetch around bottom requested, + currentUrgentLoadingStartIndex = Math.max(urgentLoadingStartIndex - FETCHING_RANGE, 0); + currentUrgentLoadingEndIndex = Math.min(urgentLoadingStartIndex + FETCHING_RANGE, size() - 1); + fetchDataInRange(jdoObjects, + currentUrgentLoadingStartIndex, + currentUrgentLoadingEndIndex); + // and around top requested objects + currentUrgentLoadingStartIndex = Math.max(urgentLoadingEndIndex - FETCHING_RANGE, 0); + currentUrgentLoadingEndIndex = Math.min(urgentLoadingEndIndex + FETCHING_RANGE, size() - 1); + fetchDataInRange(jdoObjects, + currentUrgentLoadingStartIndex, + currentUrgentLoadingEndIndex); + } + urgentLoadingAsked = false; + } + } + + if (forward) { + forward = false; + currentTopIndex = currentEndIndex; + if (currentBottomIndex > 0) { + // Still has to fetch backward + currentEndIndex = currentBottomIndex - 1; + currentStartIndex = currentEndIndex - FETCHING_RANGE; + if (currentStartIndex < 0) { + currentStartIndex = 0; + } + } else { + // Nothing more backward, let's go forward then + if (currentTopIndex < size() - 1) { + currentStartIndex = currentTopIndex + 1; + currentEndIndex = currentStartIndex + + FETCHING_RANGE; + if (currentEndIndex >= size()) { + currentEndIndex = size() - 1; + } + forward = true; + } else { + // Nothing more forward. We're done. Good job guys ! + hasDataToFetch = false; + } + } + } else { + forward = true; + currentBottomIndex = currentStartIndex; + if (currentTopIndex < size() - 1) { + // Still has to fetch forward + currentStartIndex = currentTopIndex + 1; + currentEndIndex = currentStartIndex + FETCHING_RANGE; + if (currentEndIndex >= size()) { + currentEndIndex = size() - 1; + } + } else { + // Nothing more forward, let's go backward then + if (currentBottomIndex > 0) { + currentEndIndex = currentBottomIndex - 1; + currentStartIndex = currentEndIndex + - FETCHING_RANGE; + if (currentStartIndex < 0) { + currentStartIndex = 0; + } + forward = false; + } else { + // Nothing more backward.. We're done. Good job guys + // ! + hasDataToFetch = false; + } + } + } + } + transaction.rollback(); + + } catch (TopiaException eee) { + eee.printStackTrace(); // TODO remove this + Logger.getLogger("JDOAsynchronousLoader").log(Level.SEVERE, + "Can't execute with query " + query, eee); + + } + } + + public int size() { + return dataSize; + } + + public Object getObjectAt(int index) { + if (data == null) { + return null; + } + Object result = data[index]; + if (result == null) { + // Request an urgent loading ! + synchronized (this) { + urgentLoadingAsked = true; + urgentLoadingStartIndex = Math.min(index, + urgentLoadingStartIndex); + urgentLoadingEndIndex = Math.max(index, urgentLoadingEndIndex); + } + } + return result; + } + +} \ No newline at end of file