[ 
https://issues.apache.org/jira/browse/WICKET-6286?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15804473#comment-15804473
 ] 

Jeremy commented on WICKET-6286:
--------------------------------

"thanks for participating." - Yeah, it's no problem. ;) I use wicket at work, 
and have become increasing engrossed in it, and as a result, I understand far 
too much about it. :P


Using resources is actually surprisingly easy.
I've used a couple of different systems in the past.


The first we used was is a pure direct download resource.

This one was a bit stupid, but it worked for a time, but just wasn't good 
enough.
What this was, was just a normal URL with a query parameter that pointed to a 
path, and it would just start the download for that request.
Now, obviously, we didn't just append the path as the query parameter.
We used a number of different styles.

Base64 encoded, encypted, encypted with salt, etc.

It worked, obviously, but the main issue was it relied on the client to send 
the correct data, which is never good...
So, that system had to go.

The first attempt to fix that part was a id/token system.
When a file needed to be downloaded, we'd call Download.start(target, 
outputStream);
(The start method itself was overloaded with OutputStream, File, etc)
Internally, we would generate a new token, store that token within the session, 
and return a URL that we could point the client to to start the request.
Once the request came through, we could look up the request data, and send that 
through, and wipe the token, so if the same request came through, we'd just 404 
it. (Side Note: We actually had a couple of problems with Android requesting it 
multiple times, and canceling the earlier ones. We got around this by just 
assigning a timestamp with the job so once a certain amount of time passed, 
we'd just deny it. We only do this with android, as it's the only one to do 
this.)

Now, at this point, it was decent.
It could manage downloading files, and it could do so without locking the page.

Now, at the same time, we were struggling with uploading, and the separation 
from the page.
Basically the same problem, but in reverse.
Even more so, because we were using a 3rd party library for client side 
handling, and it didn't always want to play nice with anything we had designed 
before.

The system we had used up to that point was IResourceListener, but that also 
locked the page.

It wasn't until a couple of months later that I had had a good idea.
This was the silver bullet.
It would solve both downloading and uploading, and would give us a LOT more 
control over user specific resources.

I ended up designing, what I call, 'SessionFibers'.
(I was tossing up between calling it SessionFibers, RequestFibers, or 
ResourceFibers)

Using the same token system as before, we can identify who or what started the 
request, but we can mix it in with the Session information. Specifically, 
session attributes.

I made a simple abstract class called 'AbstractSessionFiber', to create a new 
fiber, just extend this class.
(We have some classpath scanning locating various things. Such as pages, etc, 
but I'll come back to registering fibers, because there's no way we want to 
enforce a classpath scanning mechanism on all wicket instances that want to use 
a simple download tool).

I also made a second class: 'SessionFiberResource'
This is basically the gatekeeper.
It checks the token, verifies information, etc.
But this is where it differs, instead of pulling out the data it needed to 
send, as the previous system did, it pulled out an AbstractSessionFiber.

When you construct and extend an AbstractSessionFiber, you have one method you 
need to worry about implementing:

protected abstract ResourceResponse respond(final FiberAttributes attributes);

And 1 method you need to worry about calling:

public final String link();

Link does some fair basic stuff.
Generates a new token, and stores itself inside of the session using that value.
And then returns a constructed URL to point to the 'SessionFiberResource', 
which has already been mounted as a normal resource.

That link is used basically the same way as it was before.
When the request comes in, the resource handles it, pulls the 
AbstractSessionFiber out of the session, builds some basic information (That's 
the FiberAttributes. It's basically just the normal Attributes from a request, 
but contains a bit more information about the fiber), and passes the handling 
off to the Fiber.
The fiber then handles downloading, or uploading (With uploading, we store the 
data into temporary files, but because we have so much control, we can do 
anything with it).

Download and Upload are the two fibers main fibers we have.
We have some special resource ones, but it's mostly through those two.
They handle everything themselves, and without any page lock.

Obviously, it's not a silver bullet.
There's a couple of problems with it, mainly, serialisation.
There's some undesirable requirements when it comes to designing a new fiber, 
and I definitely think it's not going to be easy to describe those problems to 
users, so I don't recommend this exact system for Wicket, as you need to have 
decent knowledge of serialisation mechanics, and request processing.

If you want the code I wrote for it all, I'll be happy to help, or even just 
see if I can implement directly in wicket.

I would get into fiber registration, and my ideas on how to solve that, but 
this is already getting too long.



Also, another problem with download behaviours, is you have to locate it, or 
just over-populate your hierarchy with them.
If you're in a sub-component, and you want to download something, do you have 
to add another one?
You can get around this via a locating interface on a parent component, but 
it's not always pretty.


"yes, this seems to be the only solution currently to combine an Ajax request 
with a file download."
Ok, so this is interesting.
Javascript can't explicitly start a download.
As in, you can trigger a download, but you can't start it.
So, for example, jQuery's ajax stuff can't download something, that's why when 
you write data into an ajax response, it'll just end up in the log, or 
something, assuming you turned off wicket's response handling, otherwise it'll 
just fail.
Javascript can't say "Oh, this should be a file download".
XHR just doesn't support that, so the common ways to start a download are:
1) window.location = myUrl

This changes the browsers location, but as the response comes back, the browser 
sees it as a file download, and handles it as a download, and you don't 
navigate away.

2) Actually just click a link. As in, a fully rendered link. (This has 
literally the exact same effect as programmatically setting the location)

As the response determines what the browser should do, if it comes back as a 
download, it'll treat it as a download.




TLDR; It's quite easy to do so with Resources.
You just need to use either Session attributes, or query parameters, or both.

> Would be good to have AjaxDownload available out of the box 
> ------------------------------------------------------------
>
>                 Key: WICKET-6286
>                 URL: https://issues.apache.org/jira/browse/WICKET-6286
>             Project: Wicket
>          Issue Type: New Feature
>          Components: wicket-extensions
>    Affects Versions: 8.0.0-M2, 7.5.0
>            Reporter: Maxim Solodovnik
>            Assignee: Sven Meier
>              Labels: patch
>
> Currently every project need to create own AjaxDownload component based on 
> the Wiki example.
> Unfortunately this component has some issues with WebSockets
> Discussion: http://markmail.org/message/gizsnqh2qgypcgri
> PRs:
> https://github.com/apache/wicket/pull/190
> https://github.com/apache/wicket/pull/191



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to