Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
StreamResource in Grid for images/icons?
I'm porting a Swing application to Vaadin, and i'm currently facing an issue about showing icons/images in grid cells.
I need to display icons coming from many kind of "providers", that provide the icons through awt objects. To say it all, i extended the ImageIcon class by wrapping into it the jar resource name, so the server doesn't need to load the file but my rich swing client will do as the resources are stored into client jars. But this is not the only case, i have also custom painted icons, by drawing them at runtime (so, no resource file). And i don't really know what is coming through those providers, i can assume nearly nothing as they are not under my control.
I see that the ImageRenderer will accept only ExternalResource and ThemeResource resources, i guess this is a "limitation" due to performance issues (better delegate the browser to directly download images from the server than encode them all through the server->client channel).
But this leads to my issue. My application computes some images/icons by assembling other icons and/or painting on the target icon canvas. This makes nearly impossible to store them in jar resources or generically to file, making them available through ExternalResource and ThemeResource.
What i was hoping to find is some way to use the StreamResource. I understand that this may lead to loss of performance, especially if i don't implement some kind of server side caching to avoid rendering the same icon several time.
But being able to use StreamResource could be the incipit for a solution.
On the other hand i could use ExternalResource and render some kind of cache png files on the server file system, avoiding to replicate several times the same icon. This is not really appealing solution.
Or, again, i could use ThemeResource (sorry if i write something weird but i'm just scratching the Grid surface) and make only the static jar icons available to the grid (this leaves with a half solution anyway as i could not address the dynamic icons).
What path should i follow?
The problem is that StreamResource, ClassResource and FileResource are instances of ConnectorResource, which are handled so that the connector (typically a component) with the resource has information about the resource in a persistent form shared between the server and the client in the state. This helps keep the application secure (you cannot request a resource unless the server has explicitly made it available through the connector etc.). However, it also causes overhead and is really designed for one or a few resources for a connector, not hundreds of them for the grid. The renderers, on the other hand, use more ephemeral information in the data sent for the cells - you cannot store the StreamResource in the connector of the renderer, because the renderer will be used to render a large number of different cells in the column.
One option would be to use an ExternalResource pointing to a separate servlet or simply a separate RequestHandler within the Vaadin application, and generating suitable keys for your resources. This shouldn't be very hard, either - see https://github.com/vaadin/framework/blob/master/uitest/src/main/java/com/vaadin/tests/minitutorials/v7a1/DynamicImageUI.java for an example.
Thanks for your reply Henri,
Well yes, i supposed the restriction on the Resource class were due to performance problems.
In the meantime i tried to face my issue using the ExternalResource class, and i think i found a pretty elegant (though not very optimised) solution.
I defined a Converter for the ImageRenderer, which takes an Icon and produces an ExternalResource.
The algorithm is as follows:
I take the icon and compute some kind of "unique identifier" of the icon itself. As the Icon doesn't really have a unique identifier (but this may be addressable to some degree in my application) i simply serialize the icon and compute the SHA256 digest of the output stream. Then i use this signature as a key to find in a temporary server folder the icon png file, which incidentally is named accordingly. If i find it, i return the ExternalResource pointing that file, otherwise i write the serialized buffer and create the file. There is a risk of clash if two different icon images share the same SHA256 digest, but i see it as a very remote case.
There is an overhead because i need to serialize the icon each time to get its SHA256 digest, but considering the very small amount of code involved, at the time it looks a nice solution, that may evolve in time.
Thanks for your support!
One more suggestion in case of very small icons etc. (disclaimer: I haven't tested this myself) would be to use data URLs to encode them, sending all the information as a part of the row info. A custom renderer can be used in case ExternalResource does not do the trick.
I can see that in my temp folder the medium icon size is 400-800 bytes, with some 1-2Kb peaks. Maybe zipping the stream and B64 encoding would save me some bytes but i guess that the simple file URL requires less bytes. And saves me from a custom renderer.
Considering that a single icon can be repeated on hundreds rows, the temp folder solution looks the most acceptable so far.
Thank you for your help Henri!