DynamicImage
DynamicImage renders an image that is painted dynamically by a Java paint method using an instance of java.awt.Graphics2D much like a Swing or AWT component paints itself.
Features
- Comes with advanced expiration based caching both on session/application or client's browser to obtain best performance painting the image only once
- Provides a rich API to include image maps with tooltips and both client- and server-sided events
- Produces png and jpeg images.
- No need for gifs because transparent pngs are
shown correctly in IE
This is accomplished by displaying the image with a DIV-Tag and a Microsoft filter for those browsers that normally do not display transparent pngs correctly. - Makes it possible to use packages like JFreeChart, JCaptchaBarCode4J, jCharts or Java Advanced Imaging or whatever Graphics2D-painting API you might prefer to produce sophisticated images
- No hassles with external servlets. Just integrate your diagrams into the intuitive Tapestry flow
Possible Problems
-
HeadlessException of the like is
thrown when the Component is running on a Server without Graphical Display
Many environments, such as mainframe machines or Linux servers without a running X11, do not support a display.
These servers have to be started with-Djava.awt.headless=true
option. For more information, look here or search in the various forums about this issue.
Parameter Reference
Name | Type | Required | Default Value | Description |
---|---|---|---|---|
parameters | Object, Object[] or List | No | - | Values that may be used to characterize the image. These values form part of the caching key and are available in the painting method of the image via the context.getParameters() call. 'context' is the second argument of the painter method. |
width | int | Yes | The width/height of the image. These attributes are copied to the resulting <img>/<div>-tag of the component. Furthermore they are available in the painting method via the context.getWidth()/getHeight() call. 'context' is the second argument of the painter method. | |
height | int | Yes | ||
stateful | boolean | No | true |
If this is set to true (default), persistent state is available in the paint method.
Note, that this affects browser caching, because state maintenance changes URLs and
browsers use full URL for caching.
So switch this off, if you don't need state information in your painter method and
caching might be more effective.
See the detailed description of how to use caching effectively |
disabled | boolean | No | false | If this is set to true, no image is rendered |
hasMap | boolean | No | false | If this is set to true, an image map is rendered |
painter |
net.sf.sotacs.dynimg. painter.IPainter |
Yes |
Use the binding-prefix 'paint' to bind the painter method.
Example : painter="painter:makeDiagram"declares that the method public void makeDiagram(Graphics2D g2d, IPainterContext context)will be invoked to paint the image. |
|
listener |
org.apache.tapestry. IActionListener |
No |
The listener-method that is invoked by map areas with DirectLink action.
Example : listener="listener:onDirectLink"declares that the method public void onDirectLink(IRequestCycle cycle, String arg)will be invoked on each DirectLink event of any of the image's map areas. |
|
type | String | No | png | The image format. One of png or jpeg |
cachingMode | String | No | client |
The caching strategy.
One of client, session or application See the detailed description of how to use caching effectively |
cacheName | String | No | 's' for session caching mode,
'a' for application caching mode |
The ehcache's name to cache the image.
's' and 'a' are the preconfigured caches for session/application.
If you want to change this default, you can
specify your own ehcache configuration.
If cachingMode is client, this parameter will be ignored. See
|
Body : forbidden
Informal parameters : allowed. The informal parameters are added to the rendered HTML-IMG or DIV tag
Reserved parameters : src
How to display a Dynamic Image
To include a dynamic image on your page, use the DynamicImage component:
<img jwcid="@sotacs:DynamicImage" parameters="..." height="..." width="..." painter="painter:myMethod" />
myMethod is an arbitrary name of the paint-method that draws the image.
The paint method must have the signature
public void myMethod(java.awt.Graphics2D g, net.sf.sotacs.dynimg.api.IPainterContext)
The first argument is the Java standard graphics context, that many APIs are using.
The second argument is the component-specific context.
The interface IPainterContext gives read access to
- The image's width and height as set in the required component's width and height properties
- The image's format (jpeg or png) as set in the component's type-property
- The parameters specified in the optional parameters-property
And gives read/write access to
- The Expiration Time
Finally you can add map areas of image maps with its addMapArea mathod
How to Add an Image Map
To add image maps to the images created by DynamicImage,
- Set the hasMap-property to true
-
The image map areas are created in the paint method of the image
synchroneously while painting the image. To this aim,
MapArea
instances are added via the
addMapArea(MapArea area) method
of
IPainterContext
.
You can add rectangular, circle-shaped and polygon-shaped areas
See example code
Each map can be assigned a tooltip text and/or action events
How to Add Action Handlers to the Map
You can add both client-side (JavaScript) and server sided (DirectLink) events to the map areas and even combine them:
JavaScript Handlers
You can add JavaScript handlers to the map areas. for the events
- Click
- Double Click
- Mouse Over
- Mouse Out
There are corresponding setter methods in
MapArea
to add those events.
See example code
Add DirectLink Action
- Set a listener method to the
listener-property
The listener method must have the signature
public [void|String|IPage] ....(IRequestCycle, String)
-
For each map area, that you want to assign a DirectLink, set the
setDirectLinkArgument(String arg) method
of
MapArea
to a value that is not
null
This is accomplished in the paint-method
The direct link argument of the map area appears as 2. argument in the above server-sided listener method.
See example code
Image Maps and Transparent PNG images
How to Use Caching
Setting Expiration Time
To use caching, you must set the expiration time. This is accomplished within the painting method, calling the method public void setExpiresAt(long expires)
of the painterContext.
If you ommit that call, there is a no-cache header sent back to the client.
If expiration time is set, a corresponding Expires-At header is sent to the client. Hence the browser will cache the image with the full url as a key and won't request it anymore until expiration time is reached.
Power up Client Caching
Note that urls can be quite long and varying due to session and persistence information encoded within it. Hence browser based caching may be quite ineffective due to changing persistent client data in the url.
However, you can power this up in many situations: If you don't need explicit access to persistent client data within your image drawing method, you can make the urls shorter if you set the stateful-property to false.' This gives more effective client caching due to non-varying urls.
Note that
- It is good practice to switch off stateful' and pass parameters that characterize the image by the parameters-property of the component. Theese parameters can be retrieved in the painting method. This way, many times the resulting URLsbecome quite small compared to the full state-holding url having stateful' switched on.
- If you really need access to persistent client or session data within the painting method, don't set stateful to false.
Adding Server Sided Caching
In addition to browser caching, you can use caching on the server.
Both session and application scope can be used.
Note that
- Client's browser caching will be applied too, if you use server caching.
- Expiration time is valid for both browser and server caching
Why Server Sided Caching?
In order to avoid time consuming multiple painting of the same image, browser based caching is not enough to do effective caching.
- If an image (let's say the daily refreshing stock price graph of company Foo) is the same for all users requesting it, limitation to browser based caching would make the server repaint the same image for each new user.
- If the same image is included on more than one location in your HTML-templates, it will be painted for each location because Tapestry produces different URLs for aech location.
Session scoped Caching
To enable session-scoped caching, set the cachingMode-property to 'session'.
This will cache the produced image on the server. The caching key consists of
- The session ID
- The image format (jpg or png)
- And the value(s) set by the parameters-property.
Application scoped Caching
To enable application-scoped caching, set the cachingMode-property to 'application'.
This will cache the produced image on the server. The caching key just consists of
- The image format (jpg or png)
- And the value(s), that are set by the parameters-property .
Fine-tuning the Server Caching
The popular open source product ehcache is used to cache the images on the server.
There is a default configuration of ehcache, coming with DynamicImage. You can change that configuration
By default
- Session-scoped images use the ehcache-cache denoted by 's' (see the default configuration )
- Application-scoped images use the ehcache-cache denoted by 'a' (see the default configuration )
This can be overridden by setting the cacheName-property of DynamicImage.
Configure ehcache
See the ehcache user manual for a detailed description of how to write configuration files.
The default Configuration that comes with DynamicImage:
<ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <!-- Session scope: idle time is about the same order of magnitude as session idle time, most recently used object stay in memory: Active sessions retrieve their data from memory. Only memory cache. No Overflow to disk since elements normally do expire very quickly --> <cache name="s" maxElementsInMemory="1000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="2000" memoryStoreEvictionPolicy="LRU" /> <!-- Application scope: idle time is about 3 hours, most frequently used object stay in memory --> <cache name="a" maxElementsInMemory="1000" eternal="false" overflowToDisk="true" diskPersistent="false" timeToIdleSeconds="10000" memoryStoreEvictionPolicy="LFU" /> <!-- This is a special internal cache that caches images with maps that are not explicitely cached on session or application scope. This is necessary because the paint method shall be executed only once whereas image information is needed in two subsequent requests. 1 : the image-map is written in a first request together with the img-tag 2 : In a second request the binary image data is requested. idle time 2 minutes must be enough to hold the data for subsequent requests if the first request is processed very slowly (more than two minutes), the paint method is called twice. --> <cache name="browserCachedMappedImage" maxElementsInMemory="1000" eternal="false" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="120" timeToLiveSeconds="120" memoryStoreEvictionPolicy="LRU" /> </ehcache>
If you want to change the default
- Write your configuration file and put it on the application's classpath
- Add the following line to your .application file <meta key="net.sf.sotacs.dynimg.cache.ehcache-config" value="pathToTheConfigFile"/> where pathToTheConfigFile denotes the path to load the ressource from the class path.
Cache Idle-Time and Expiration Time
The timeToIdleSeconds configuration property of ehcache must not be confused with the image's expiration time, being set in the painter method.
The cache's timeToIdleSeconds-time just removes sparsely requested images from cache. This may happen even if the image still did not time out.
From sotacs 0.85 on, it is no longer necesary to set the timeToIdleSeconds, since expired elements are automatically removed from ehcache >1.2.1
Trace the Caching
Dynamic Image is logging some information:
- New request for an image
- Image type, caching mode
- caching key
- Whether the image could be retrieved from cache or not
- Expiration information
- Whether the image is put into the cache or not
Jakarta Commons Logging is used to log this data.
- Logger name : net.sf.sotacs.DynamicImage
- Level : debug
The easiest way to make the information appear on the console is to set the system properties:
org.apache.commons.logging.log=org.apache.commons.logging.impl.SimpleLog org.apache.commons.logging.simplelog.log.net.sf.sotacs.DynamicImage=debug
Access the Cache per API
There is a HiveMind service to access the ehcache-CacheManager instance that is used in the DynamicImage component.
- Name of the service : net.sf.sotacs.DynamicImageCacheService
- Interface : net.sf.sotacs.dynimg.cache.ICacheService
For example you can inject the service into your pages like this:
@InjectObject("service:net.sf.sotacs.DynamicImageCacheService") public abstract net.sf.sotacs.dynimg.cache.ICacheService getCacheService();
The getCacheManager() method of net.sf.sotacs.dynimg.cache.ICacheService gives you access to the net.sf.ehcache.CacheManager instance of ehcache.
For example this can be used to retrieve current sizes of memory/disk space used by the different caches.