sourcforge > sotacs
 

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

  1. 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

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,

  1. Set the hasMap-property to true
  2. 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

  1. Set a listener method to the listener-property The listener method must have the signature
     public [void|String|IPage] ....(IRequestCycle, String)
    
  2. 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

Warning
It is impossible to render transparent PNG images together with image maps in MSIE <=6.0. So if you use image maps, don't forget to paint the background.

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

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

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

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.