Hei Sascha i got positive feedback from my colleaque on the changes of the threading stuff. If Michael aggrees too, you can commit
stefan Sascha L. Teichmann schrieb: > Hi! > > Stefan Steiniger schrieb: >> Similar to Landon I have never touched threading nor heared any lectures >> or read books on it. Thus .. i dont know if it turns out to be good or bad. >> But i know that making things more clean is good objective. Thus, i >> support the changes. It would be good when we finally change the code, >> that you also put into the code some docs or references where you >> outline why somethings has changed. (actually I currently would opt to >> leave the old code as comment and not to remove entirely) > > The ThreadQueue is JavaDoc'ed but I can add some > design notes as well. Keeping old things as comments negates > the existence of version control systems. > CVS is exactly the time machine you for this. > As I pointed out earlier having a ChangeLog would > be nice to document motivations and backgrounds for a change. Simply > generating a ChangeLog out of the commit messages is convenient but > it does not uncover the true potential of such a 'log' file. > >> Due to my lack of knowledge i forwarded your email to a colleague who >> developed a web-service based on JUMP (which runs on a server, see [1]), >> an ajax client and recently did some multiprocessor experiments for the >> web-services processing. > > I look forward to his comments. > >> I hope 1) he will look on your proposal and 2) you can wait some days >> until he, Larry and Michael got an opinion. > > @Michael: You ask what to change. For the moment just replace > ThreadQueue.java with one I've sent last. The first patches > are addressing the problem with ThreadQueue limited to one > Thread at a time (defaultRenderingThreadQueue in RenderingManger). > You can apply these patches too, but they are not needed any > longer. With next patch I'll send the this second thread queue > will be removed from RenderingManager entirely. > > What to test? Doing your day-to-day is the best thing > you can do. The most important thing is that the new one > behaves exactly like the old one. The magic word is compatibility. > I don't want to break the show. Use plug-ins, use many layers, > use less layer, use db and WMS layers. Do all the fancy stuff > you like to do. If the OJ freezes or something other looks odd > just tell me. :-) > > - Sascha > >> Michaël Michaud schrieb: >>> Sascha L. Teichmann a écrit : >>> >>>> Really back to topic: >>>> >>>> Find attached a replacement for ThreadQueue [1]. >>>> To use it just overwrite the original one. >>>> >>>> >>> Hi Sascha : >>> >>> I think that trying to have a cleaner and more simple code is an >>> excellent goal, and I'd like to help, but I'm not sure I can understand >>> all these thread issues. >>> If you tell me exactly which classes I must replace (on ly ThreadQueue >>> or also the pieces of code from your previous mail) and what kind of >>> tests I should do (rendering different kind of layers ? mixing different >>> kind of layers), I'll try to make some more tests on my desktop. >>> >>> Thanks, for the hard work >>> >>> Michaël >>> >>>> This one works for the real parallel case of >>>> layer rendering too. Each time a thread finished >>>> executing its Runnable it looks into the queue >>>> if they are more jobs to do. This prevents unnecessary >>>> thread creations/shutdowns. If the queue is empty >>>> the worker thread is kept alive for 5 seconds waiting >>>> for new jobs. This results in a kind of thread pooling. >>>> >>>> @Larry: I've isolated my implementation and the OJ >>>> ThreadQueue and done a synthetic benchmark with a >>>> larger number of jobs (10,000+). My implementation >>>> works about two orders faster than the OJ one. >>>> But this is of little meaning because OJ only >>>> has to handle a number of jobs equal the number >>>> of layers. This will hardly hit 10,000+ ;-) >>>> But again: My mission is improve the structure not >>>> primarily the speed. >>>> >>>> I've tested the new ThreadQueue to some extent but >>>> I'am male(tm) after all ... potentially making mistakes. >>>> It would be very kind if someone test it too. >>>> >>>> My next step will be some clean up in the RenderingManager [2]. >>>> I'am not sure that it is really needed to have two ThreadQueues >>>> there. The effect of the single tread one can be easily >>>> simulated with a data structure like the RunnableArrayList which >>>> I've posted already. >>>> >>>> Any comments? >>>> >>>> Yours, >>>> Sascha >>>> >>>> [1] com.vividsolutions.jump.workbench.ui.renderer.ThreadQueue >>>> [2] com.vividsolutions.jump.workbench.ui.renderer.RenderingManager >>>> >>>> Sunburned Surveyor schrieb: >>>> >>>> >>>>> Sascha, >>>>> >>>>> Please accept my sincerest aopologies. I'm afriad my American >>>>> ignorance of other cultures is more than just a little obvious at >>>>> times. >>>>> >>>>> I believe I have made the same mistake with Jan. :] >>>>> >>>>> Please be patient with me as I learn the details of cultures across >>>>> the Pacific and Atalantic Oceans! >>>>> >>>>> The Sunburned Surveyor >>>>> >>>>> On 5/24/07, Sascha L. Teichmann <[EMAIL PROTECTED]> wrote: >>>>> >>>>> >>>>>> TNX, but for the records 'he' would be more suited in my case. >>>>>> >>>>>> 'Sascha' is basically a Russian term of endearment for the >>>>>> boys name 'Alexander' but it's also used as a girls name. >>>>>> >>>>>> BTW: 'Jan' is a girls name in the US too, isn't? ;-) >>>>>> >>>>>> - Sascha >>>>>> >>>>>> >>>>>> Sunburned Surveyor schrieb: >>>>>> >>>>>> >>>>>>> Sascha and Larry, >>>>>>> >>>>>>> I must admit that I am way over my head here. I haven't done much >>>>>>> thread programming in Java. (Hopefully Stefan has!) >>>>>>> >>>>>>> Sascha wrote: "My primary goal is to simplify the >>>>>>> threading code to make it more reliable in terms of time." >>>>>>> >>>>>>> This seems like an admirable goal to me. If Larry, or a similar >>>>>>> programmer of his experience, agrees that this changes would be >>>>>>> beneficial, I say we give Sascha a shot at it. It sounds like she has >>>>>>> considered her changes carefully. >>>>>>> >>>>>>> Just my two cents. >>>>>>> >>>>>>> The Sunburned Surveyor >>>>>>> >>>>>>> >>>>>>> >>>>>>> On 5/24/07, Sascha L. Teichmann <[EMAIL PROTECTED]> wrote: >>>>>>> >>>>>>> >>>>>>>> Hi Larry, >>>>>>>> >>>>>>>> short answer first: No, I don't have any benchmarks, yet. >>>>>>>> >>>>>>>> The long answer: My primary goal is to simplify the >>>>>>>> threading code to make it more reliable in terms of time. >>>>>>>> Gaining performance improvements would be a neat side effect. >>>>>>>> >>>>>>>> Background: Multi-threading may be fine for slow layers >>>>>>>> which arrives later on the screen but for exporting >>>>>>>> the data (to print/layout e.g) it would be nice to have >>>>>>>> them arriving one after the other. >>>>>>>> >>>>>>>> My final goal is to have a simple switch between the normal >>>>>>>> and the serial mode. To archive that I try to carefully >>>>>>>> refactor the system doing little patches step by step >>>>>>>> not to break it. Refactoring the ThreadQueue with it's >>>>>>>> 'flaws' seems a to be a good starting point to me. >>>>>>>> >>>>>>>> One reason for this change is the fact that you are able >>>>>>>> to figure out if the default thread runs empty. But there >>>>>>>> is no way to figure out when the last thread ends. That >>>>>>>> are different things. A mechanism for this is planned. >>>>>>>> >>>>>>>> Sorry, if I've shadowed my true intentions to much. Maybe >>>>>>>> I should discuss more before I send patches. ;-) >>>>>>>> >>>>>>>> Back to the technical side: >>>>>>>> >>>>>>>> The patch needs some testing but I don't expect too much >>>>>>>> performance improvement. >>>>>>>> >>>>>>>> A less intrusive alternative to bind the Runnables that go to the >>>>>>>> default ThreadQueue into one thread is to create a container >>>>>>>> which self is Runnable (see e.g. RunnableArrayList attached) >>>>>>>> and put them into an instance of this class. >>>>>>>> This container is put into multiRendererThreadQueue as a Runnable. >>>>>>>> With this modification the defaultRendererThreadQueue can >>>>>>>> be removed (multiRendererThreadQueue renamed to >>>>>>>> defaultRendererThreadQueue). Only an idea ... I'm in discussion >>>>>>>> mode now. ;-) >>>>>>>> >>>>>>>> - Sascha >>>>>>>> >>>>>>>> >>>>>>>> Larry Becker schrieb: >>>>>>>> >>>>>>>> >>>>>>>>> Hi Sascha, >>>>>>>>> >>>>>>>>> I read your comments and look at your code with interest. It appears >>>>>>>>> to be an improved ThreadQueue implementation, but will require a lot >>>>>>>>> of >>>>>>>>> testing to verify. Before I invest this time, I would like to know >>>>>>>>> what >>>>>>>>> problem it is solving. I see your dislikes a - e, but these are not >>>>>>>>> really problems, only architectural critiques. Have you done any >>>>>>>>> benchmarks that show that the new SingleThreadQueue speeds up >>>>>>>>> rendering? Your logical argument that it should be more efficient is >>>>>>>>> persuasive, but I have been surprised by Java before. >>>>>>>>> >>>>>>>>> respectfully, >>>>>>>>> Larry Becker >>>>>>>>> >>>>>>>>> On 5/23/07, *Sascha L. Teichmann* <[EMAIL PROTECTED] >>>>>>>>> <mailto:[EMAIL PROTECTED]>> wrote: >>>>>>>>> >>>>>>>>> Hi together, >>>>>>>>> >>>>>>>>> as some of you may already know i have my dislikes against >>>>>>>>> ThreadQueue [1] (Hi, Larry!) see my mail [2] >>>>>>>>> >>>>>>>>> a - It forks a new thread for any Runnable it processes. >>>>>>>>> b - It has an ugly busy wait loop inside. >>>>>>>>> c - The event listener for empty queue fires to often. >>>>>>>>> d - The default ThreadQueue is some kind of thread serializer. >>>>>>>>> e - The DB/WMS ThreadQueue has no public access. >>>>>>>>> >>>>>>>>> Now I've written a sub class of ThreadQueue: SingleThreadQueue >>>>>>>>> (see attachment). This one deals with the issues a, b and d. >>>>>>>>> I also attached a patch against RenderingManager [3] to handle e. >>>>>>>>> >>>>>>>>> The new class (to be placed in package >>>>>>>>> com.vividsolutions.jump.workbench.ui.renderer) is a drop-in >>>>>>>>> replacement for the default ThreadQueue in RenderingManager. >>>>>>>>> Not for the ThreadQueue that handles the DB/WMS layers. >>>>>>>>> >>>>>>>>> Because Jon limited the number of parallel threads in default >>>>>>>>> queue to 1 I see no reason why to fork a new thread for each >>>>>>>>> Runnable it processes. Thread creation/shutdown is fairly >>>>>>>>> expensive. Instead a single background thread is started >>>>>>>>> which processes the Runnables one by one. If the thread >>>>>>>>> is idle for 30 secs it shuts itself down. If you have a lot >>>>>>>>> of (non-WMS/BB) layers this should improve performance >>>>>>>>> and save some resources. The processing itself is done >>>>>>>>> with a monitor (synchronized/wait/notify) so there is no >>>>>>>>> busy wait any more. >>>>>>>>> >>>>>>>>> The DB/WMS ThreadQueue (real parallel threads) is left untouched >>>>>>>>> for the moment. Depending on my personal schedule I will send >>>>>>>>> a patch against this one too. Preliminary code with thread pooling >>>>>>>>> exists but it needs a bit more testing. >>>>>>>>> >>>>>>>>> Find attached the new class and patches against RenderingManager >>>>>>>>> and >>>>>>>>> the old ThreadQueue to bring it to work. >>>>>>>>> >>>>>>>>> Comments are very welcome. :-) >>>>>>>>> >>>>>>>>> Kind regrads, >>>>>>>>> Sascha >>>>>>>>> >>>>>>>>> [1] com.vividsolutions.jump.workbench.ui.renderer.ThreadQueue >>>>>>>>> [2] >>>>>>>>> >>>>>>>>> http://sourceforge.net/mailarchive/message.php?msg_name=4653389E.6000706%40intevation.de >>>>>>>>> [3] com.vividsolutions.jump.workbench.ui.renderer.RenderingManager >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> ------------------------------------------------------------------------ >>>>>>>>> >>>>>>>>> /* >>>>>>>>> * The Unified Mapping Platform (JUMP) is an extensible, interactive >>>>>>>>> GUI >>>>>>>>> * for visualizing and manipulating spatial features with geometry and >>>>>>>>> attributes. >>>>>>>>> * >>>>>>>>> * Copyright (C) 2003 Vivid Solutions >>>>>>>>> * Copyright (C) 2007 Intevation GmbH >>>>>>>>> * >>>>>>>>> * 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. >>>>>>>>> * >>>>>>>>> * Suite #1A >>>>>>>>> * 2328 Government Street >>>>>>>>> * Victoria BC V8T 5G5 >>>>>>>>> * Canada >>>>>>>>> * >>>>>>>>> * (250)385-6040 >>>>>>>>> * www.vividsolutions.com >>>>>>>>> */ >>>>>>>>> package com.vividsolutions.jump.workbench.ui.renderer; >>>>>>>>> >>>>>>>>> import java.util.LinkedList; >>>>>>>>> import java.util.ArrayList; >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * This thread queue executes at maximum N Runnables in parallel >>>>>>>>> * were N is a given number of worker threads that should be used. >>>>>>>>> * If N threads are running and busy each further incoming >>>>>>>>> * Runnable is queued until one of the threads has finished its >>>>>>>>> current job. >>>>>>>>> * If a worker thread becomes idle (no more job in the queue) >>>>>>>>> * it is hold alive for 5 seconds. If during this period of time >>>>>>>>> * no new Runnable is enqueued the worker thread dies. >>>>>>>>> * >>>>>>>>> * @author Sascha L. Teichmann ([EMAIL PROTECTED]) >>>>>>>>> */ >>>>>>>>> public class ThreadQueue >>>>>>>>> { >>>>>>>>> /** The time a worker thread stays alive if idle */ >>>>>>>>> public static final long WORKER_STAY_ALIVE_TIME = 5000L; >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Worker thread. Fetches Runnable from the surrounding >>>>>>>>> * ThreadQueue instance. >>>>>>>>> */ >>>>>>>>> protected class Worker >>>>>>>>> extends Thread >>>>>>>>> { >>>>>>>>> public void run() { >>>>>>>>> try { >>>>>>>>> for (;;) { >>>>>>>>> Runnable runnable; >>>>>>>>> >>>>>>>>> synchronized (queuedRunnables) { >>>>>>>>> if >>>>>>>>> (queuedRunnables.isEmpty()) { >>>>>>>>> >>>>>>>>> ++waitingThreads; >>>>>>>>> try { >>>>>>>>> >>>>>>>>> queuedRunnables.wait(WORKER_STAY_ALIVE_TIME); >>>>>>>>> } >>>>>>>>> catch >>>>>>>>> (InterruptedException ie) { >>>>>>>>> } >>>>>>>>> finally { >>>>>>>>> >>>>>>>>> --waitingThreads; >>>>>>>>> } >>>>>>>>> >>>>>>>>> // if still >>>>>>>>> empty -> die! >>>>>>>>> if >>>>>>>>> (queuedRunnables.isEmpty()) >>>>>>>>> break; >>>>>>>>> } >>>>>>>>> if (disposed) >>>>>>>>> break; >>>>>>>>> runnable = >>>>>>>>> (Runnable)queuedRunnables.remove(); >>>>>>>>> } // synchronized >>>>>>>>> queuedRunnables >>>>>>>>> >>>>>>>>> try { >>>>>>>>> runnable.run(); >>>>>>>>> } >>>>>>>>> catch (Exception e) { >>>>>>>>> e.printStackTrace(); >>>>>>>>> } >>>>>>>>> } // for (;;) >>>>>>>>> } >>>>>>>>> finally { // guarantee that counter goes down >>>>>>>>> boolean allRunningThreadsFinished; >>>>>>>>> synchronized (runningThreads) { >>>>>>>>> allRunningThreadsFinished = >>>>>>>>> --runningThreads[0] == 0; >>>>>>>>> } >>>>>>>>> if (allRunningThreadsFinished) >>>>>>>>> fireAllRunningThreadsFinished(); >>>>>>>>> } >>>>>>>>> } >>>>>>>>> } // class Worker >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * If the number of running threads goes down to zero >>>>>>>>> * implementations of this interface are able to be informed. >>>>>>>>> */ >>>>>>>>> public interface Listener { >>>>>>>>> void allRunningThreadsFinished(); >>>>>>>>> } // interface Listener >>>>>>>>> >>>>>>>>> /** Number of running threads */ >>>>>>>>> protected int [] runningThreads = new int[1]; >>>>>>>>> >>>>>>>>> /** max. Number of threads running parallel */ >>>>>>>>> protected int maxRunningThreads; >>>>>>>>> >>>>>>>>> /** Number of threads that are currently idle */ >>>>>>>>> protected int waitingThreads; >>>>>>>>> >>>>>>>>> /** The queue of Runnables jobs waiting to be run */ >>>>>>>>> protected LinkedList queuedRunnables; >>>>>>>>> >>>>>>>>> /** Singals that the ThreadQueue is going to quit */ >>>>>>>>> protected boolean disposed; >>>>>>>>> >>>>>>>>> /** List of Listeners */ >>>>>>>>> protected ArrayList listeners = new ArrayList(); >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Creates a ThreadQueue with one worker thread. >>>>>>>>> */ >>>>>>>>> public ThreadQueue() { >>>>>>>>> this(1); >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** Creates a ThreadQueue with a given number of worker threads. >>>>>>>>> * @param maxRunningThreads the max. number of threads to be >>>>>>>>> run parallel. >>>>>>>>> */ >>>>>>>>> public ThreadQueue(int maxRunningThreads) { >>>>>>>>> this.maxRunningThreads = Math.max(1, maxRunningThreads); >>>>>>>>> queuedRunnables = new LinkedList(); >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Adds a Listener to this ThreadQueue. >>>>>>>>> * @param listener the listener to add. >>>>>>>>> */ >>>>>>>>> public synchronized void add(Listener listener) { >>>>>>>>> if (listener != null) >>>>>>>>> listeners.add(listener); >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Removes a Listener from this ThreadQueue. >>>>>>>>> * @param listener the listener to be removed. >>>>>>>>> */ >>>>>>>>> public synchronized void remove(Listener listener) { >>>>>>>>> if (listener != null) >>>>>>>>> listeners.add(listener); >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Informs Listeners of the fact that the number of running >>>>>>>>> threads >>>>>>>>> * went to zero. >>>>>>>>> */ >>>>>>>>> protected void fireAllRunningThreadsFinished() { >>>>>>>>> ArrayList copy; >>>>>>>>> synchronized (this) { copy = new ArrayList(listeners); } >>>>>>>>> for (int i = copy.size()-1; i >= 0; --i) >>>>>>>>> >>>>>>>>> ((Listener)copy.get(i)).allRunningThreadsFinished(); >>>>>>>>> } >>>>>>>>> >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * The number of currently running worker threads. >>>>>>>>> * @return number of currently running worker threads. >>>>>>>>> */ >>>>>>>>> public int runningThreads() { >>>>>>>>> synchronized (runningThreads) { >>>>>>>>> return runningThreads[0]; >>>>>>>>> } >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * The number of currently waiting Runnables. >>>>>>>>> * @return number of currently waiting Runnables. >>>>>>>>> */ >>>>>>>>> public int waitingRunnables() { >>>>>>>>> synchronized (runningThreads) { >>>>>>>>> return queuedRunnables.size(); >>>>>>>>> } >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * The number of currently idle worker threads. >>>>>>>>> * @return number of currently idle worker threads. >>>>>>>>> */ >>>>>>>>> public int waitingThreads() { >>>>>>>>> synchronized (queuedRunnables) { >>>>>>>>> return waitingThreads; >>>>>>>>> } >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Adds a Runnables to the queue. It will be run in one >>>>>>>>> * of the worker threads. >>>>>>>>> * @param runnable The Runnables to add >>>>>>>>> */ >>>>>>>>> public void add(Runnable runnable) { >>>>>>>>> int waiting; >>>>>>>>> synchronized (queuedRunnables) { >>>>>>>>> if (disposed) >>>>>>>>> return; >>>>>>>>> waiting = waitingThreads; >>>>>>>>> queuedRunnables.add(runnable); >>>>>>>>> queuedRunnables.notify(); >>>>>>>>> } // synchronized (queuedRunnables) >>>>>>>>> >>>>>>>>> synchronized (runningThreads) { >>>>>>>>> >>>>>>>>> // if waitingThreads == 1 then >>>>>>>>> // the queuedRunnables.notify() should have >>>>>>>>> waked it up. >>>>>>>>> >>>>>>>>> if (waitingThreads < 2 && runningThreads[0] < >>>>>>>>> maxRunningThreads) { >>>>>>>>> ++runningThreads[0]; >>>>>>>>> Worker w = new Worker(); >>>>>>>>> w.setDaemon(true); >>>>>>>>> w.start(); >>>>>>>>> } >>>>>>>>> } // synchronized (runningThreads) >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Empties the queue of waiting Runnables. >>>>>>>>> */ >>>>>>>>> public void clear() { >>>>>>>>> synchronized (queuedRunnables) { >>>>>>>>> queuedRunnables.clear(); >>>>>>>>> } >>>>>>>>> } >>>>>>>>> >>>>>>>>> /** >>>>>>>>> * Shuts down the ThreadQueue. >>>>>>>>> */ >>>>>>>>> public void dispose() { >>>>>>>>> synchronized (queuedRunnables) { >>>>>>>>> disposed = true; >>>>>>>>> queuedRunnables.clear(); >>>>>>>>> // wakeup idle threads >>>>>>>>> queuedRunnables.notifyAll(); >>>>>>>>> } >>>>>>>>> synchronized (this) { >>>>>>>>> listeners.clear(); >>>>>>>>> } >>>>>>>>> } >>>>>>>>> } >>>>>>>>> // end of file >>>>>>>>> >>>>>>>>> >>>>>>>>> ------------------------------------------------------------------------ >>>>>>>>> >>>>>>>>> ------------------------------------------------------------------------- >>>>>>>>> This SF.net email is sponsored by DB2 Express >>>>>>>>> Download DB2 Express C - the FREE version of DB2 express and take >>>>>>>>> control of your XML. No limits. Just data. Click to get it now. >>>>>>>>> http://sourceforge.net/powerbar/db2/ >>>>>>>>> >>>>>>>>> ------------------------------------------------------------------------ >>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> Jump-pilot-devel mailing list >>>>>>>>> Jump-pilot-devel@lists.sourceforge.net >>>>>>>>> https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel >>>>>>>>> >>>>>>>>> >>> ------------------------------------------------------------------------- >>> This SF.net email is sponsored by DB2 Express >>> Download DB2 Express C - the FREE version of DB2 express and take >>> control of your XML. No limits. Just data. Click to get it now. >>> http://sourceforge.net/powerbar/db2/ >>> _______________________________________________ >>> Jump-pilot-devel mailing list >>> Jump-pilot-devel@lists.sourceforge.net >>> https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel >>> >>> >> ------------------------------------------------------------------------- >> This SF.net email is sponsored by DB2 Express >> Download DB2 Express C - the FREE version of DB2 express and take >> control of your XML. No limits. Just data. Click to get it now. >> http://sourceforge.net/powerbar/db2/ >> _______________________________________________ >> Jump-pilot-devel mailing list >> Jump-pilot-devel@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel > > ------------------------------------------------------------------------- > This SF.net email is sponsored by DB2 Express > Download DB2 Express C - the FREE version of DB2 express and take > control of your XML. No limits. Just data. Click to get it now. > http://sourceforge.net/powerbar/db2/ > _______________________________________________ > Jump-pilot-devel mailing list > Jump-pilot-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel > > ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel