Maybe I don't see something, but I think it is much more simpler. Here it is:
When the processing of RequestElement starts, the ClientRequest object
is already activated, because FCPClient:305 activates all requests,
and puts to the v List, and those object are the very same objects
RequestElement works with. So they are initialized, but maybe the
Fetch(Insert)Context isn't. So activate it. And also the EventProducer
if not activated. As the constructor has the ObjectContainer object,
it can activate. As I see this is used at ClientGet:785. invoked from
RequestElement:122 , and it seems to be working.
The only problem seems to be that these listeners shouldn't be
persisted, they aren't supposed to survive a restart. So I think I
should create a mechanism for transient eventlisteners.
What you think about it?
sashee
On Tue, Jul 7, 2009 at 7:56 PM, Matthew
Toseland<toad at amphibian.dyndns.org> wrote:
> On Tuesday 07 July 2009 18:50:58 Matthew Toseland wrote:
>> On Tuesday 07 July 2009 18:26:40 Matthew Toseland wrote:
>> > ? ? ?public void addEventListener(ClientEventListener cel) {
>> > + ? ? ? if(listeners==null){
>> > + ? ? ? ? ? ? ? //Don't know how it can happen, but it did, and checking
>> > for null isn't going to hurt anything
>> > + ? ? ? ? ? ? ? listeners=new Vector<ClientEventListener>();
>> > + ? ? ? }
>> > ? ? ? ? if(cel != null)
>> > ? ? ? ? ? ? listeners.addElement(cel);
>> > ? ? ? ? else
>> > ? ? ? ? ? ? throw new IllegalArgumentException("Adding a null listener!");
>> > ? ? ?}
>> >
>> > It can't happen for transient requests. For persistent requests, a
>> > persistence-related bug in the code might well cause it to happen however.
>> > :(
>> >
>> Okay, now I see what is going on ...
>>
>> + ? ? ? public RequestElement(ClientRequest clientRequest, int[] columns,
>> String path, ObjectContainer container, boolean advancedModeEnabled,
>> String[] priorityClasses, boolean isUpload, ToadletContext ctx) {
>> ...
>> + ? ? ? ? ? ? ? if (clientRequest instanceof ClientGet) {
>> + ? ? ? ? ? ? ? ? ? ? ? if (((ClientGet)
>> clientRequest).getFetchContext().eventProducer != null) ((ClientGet)
>> clientRequest).getFetchContext().eventProducer.addEventListener(progressListener);
>> + ? ? ? ? ? ? ? } else if (clientRequest instanceof ClientPutBase) {
>> + ? ? ? ? ? ? ? ? ? ? ? if (((ClientPutBase)
>> clientRequest).getInsertContext().eventProducer != null) ((ClientPutBase)
>> clientRequest).getInsertContext().eventProducer.addEventListener(progressListener);
>> + ? ? ? ? ? ? ? } else {
>> + ? ? ? ? ? ? ? ? ? ? ? System.err.println("Dont know this type! type:" +
>> clientRequest.getClass());
>> + ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ?
>> clientRequest.getClient().addRequestCompletionCallback(progressListener);
>> + ? ? ? }
>>
>>
>> Anything on the download/upload page is a persistent request. A persistent
>> request is either persistence=reboot or persistence=forever, but the latter
>> is far more common. These requests are *stored in a database*, and are not
>> always in memory. Using them as variables will cause them to be pinned in
>> memory but will not ensure they stay "activated" i.e. that all their fields
>> are loaded and non-null. The only safe way to access them is to schedule a
>> job on the database thread (the DBJobRunner, aka NodeClientCore). Hence, the
>> SimpleEventProducer may well be null, and even if it isn't, it may not be
>> activated, meaning the listeners array may be null.
>>
>> Hence subscribing to the SimpleEventProducer and listening for events is not
>> going to be sufficient. You need to implement some sort of global hook, so
>> that when an event is generated against a request in the global queue (only
>> the global queue is shown on the downloads/uploads pages), you get a
>> notification, at that point, while the request is still active.
>>
>> What you need to do is as follows:
>> - FCPServer.globalRebootClient is the half of the global queue that is
>> persistence=reboot, that is, the part that isn't persisted to the database.
>> You can ignore it for all practical purposes, but you should really deal
>> with it when you get around to it.
>> - FCPServer.globalForeverClient is the half of the global queue that is
>> persistence=forever, and in practice has almost all global requests
>> (requests or inserts shown on the downloads/uploads pages). It is never
>> deactivated.
>> - This is an FCPClient.
>> - You need to create a callback method and a registry of listeners on
>> FCPClient that gets fed every FCP event, at the time it is created, along
>> with the ClientRequest which created it. This happens in ClientGet.receive,
>> ClientPutBase.receive, and probably that's about it; check the call
>> hierarchy for the constructors for e.g. SimpleProgressMessage if you are
>> concerned. This always happens on the database thread, or at least, if it's
>> not on the database thread, it schedules a job on the database thread to
>> send the message (e.g. in ClientPutBase.trySendProgressMessage). So call the
>> callback at that point.
>> - The callback should go to some sort of manager object, which determines
>> which RequestElement(s) want the message. Or you can just register all of
>> them but it will be slower that way.
>> - Don't store the pointers, store the UID of the request (this is because we
>> don't want to pin the request object in memory). This is an long value for a
>> specific request in the database which doesn't change unless we defrag
>> (which is an offline operation). You can get it by:
>>
>> container.ext().getID(<object>).
>>
>> Please let me know if you need any more information on this! The db4o zip
>> file contains javadocs, you can get it from db4o.com, we use version 7.4.
>>
> Also, you should not store the ObjectContainer, and should not access the
> database except on the database thread (i.e. when an ObjectContainer has been
> passed in ultimately from a DBJob).
>
> _______________________________________________
> Devl mailing list
> Devl at freenetproject.org
> http://emu.freenetproject.org/cgi-bin/mailman/listinfo/devl
>