Issue Details (XML | Word | Printable)

Key: GEOT-862
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: Jesse Eichar
Reporter: Mark Presling
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
GeoTools

ImageLoader not thread safe

Created: 30/May/06 06:14 AM   Updated: 02/Jun/06 04:18 PM
Component/s: core render
Affects Version/s: 2.2-RC3
Fix Version/s: None

File Attachments: 1. Text File imageloaderbug.log (42 kB)

Environment: Experienced under Windows, Linux, Java 1.5
Issue Links:
Duplicate
 


 Description  « Hide
I have been developing an Eclipse RCP application based on uDig plugins. I have discovered a bug in the way ExternalGraphic images are loaded by the StreamingRenderer when rendering a layer with more than approx. 150 features and lots of SLD rules.

Now, clarification time:

  • The layer is a WFS layer
  • It's data source has about 200 features in it at the moment, although the issue appears randomly as the numbers increase from 0
  • The SLD has around 8 rules in it, each one displaying a different icon of 25x25 depending on filtering of attributes of each feature
  • Each time the application is run the map is rendered with different outcomes:
    o One of the icons may not load (meaning that multiple features of the same type do not render)
    o Multiple icons may not display (meaning that multiple features of the same type do not render)
    o One of the icons may be loaded for two or more other icons (meaning that every feature can be rendered, but with the wrong icon)

My findings:

  • org.geotools.renderer.style.SLDStyleFactory contains a static instace of org.geotools.renderer.style.ImageLoader
  • That image loader is used to load ExternalGraphic images from SLDStyleFactory.getImage(ExternalGraphic, int)
  • ImageLoader caches loaded images and will return an existing instance if it has already been loaded
  • If it has not been loaded, it will then spawn a new Thread with itself as the Runnable
  • The thread then loads the image with the help of a static instance of a java.awt.MediaTracker
  • Sometimes when the get method is called multiple times it is likely that the cache gets set with a null value for the url key and that is always returned, even though the logging shows that the image loaded correctly
  • Loading images from http:// (remote server) or file:// urls makes no difference to the problem (ie, latency/loading time isn't a factor)
  • Making the ImageLoader.get(URL, boolean) method synchronized fixes all the image loading problems

Adding the "synchronized" to the method certainly works, but I am not clear on the performance impact this may have if the number of features is very large. It does not seem to affect our application significantly enough to notice, based on several hundred features. It would pay for someone to put some thought around what other components use this mechanism for loading images, as I have not got as deep as how the render actually works.

As this is based on uDig 1.1.x plugins, this problem exists in the 2.2.x branch. I don't know whether it has been resolved in the latest code. An SVN diff between 2.2.x and trunk show no differences in ImageLoader.java.

I have attached a log file which shows the Level.FINEST output for the logger used by ImageLoader class. In this case only 2 out of 3 (or 4) images loaded correctly. The rest of the time the get() method returns null for the features of that type.

Searching shows up GEOT-611 (http://jira.codehaus.org/browse/GEOT-611) created in Jul 2005 that appears to be the same thing, but nothing has been done about it.



 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Jesse Eichar added a comment - 30/May/06 11:08 AM
thanks mark I was goig to ffix this yesterday but got distracted by a geotools meeting of doom! I'll try to get it done today

Jesse Eichar added a comment - 30/May/06 11:16 AM
I'm thinking the most efficient way would to have 2 locks. 1 for checking the cache and another for the add method. that way cached images can still be retrieved while an image is being loaded. proper checks at te start of the add method would ensure that an image is not loaded more than 1 time

Jesse Eichar added a comment - 30/May/06 12:40 PM
fixed on 2.2.x version 19728

I'm going to upload to the list server. Get the latest version and make sure my changes work... don't cause deadlocks etc...

Jesse Eichar added a comment - 30/May/06 12:40 PM
ps. thank you in advance.

Jesse Eichar added a comment - 30/May/06 01:14 PM
ok its ready for download

Mark Presling added a comment - 01/Jun/06 10:59 PM
Sorry Jesse... I just tried it and it doesn't work at all now (:

The problem is that the following code in get() is called on the first attempt to get an image:
            if (!interactive) {
                images.put(location, null);
            }

So when it's the first request for an image that code is called, then add() is called. add() contains this check:
        synchronized (images) {
            if( images.containsKey(location) )
                return;
        }

And then get() does this:
        return (BufferedImage) images.get(location);

Commenting out the first if (!interactive) fixes the problem. I guess having a not null check in add() would be the better option. I don't really understand why you would always want to return null, but there must have been a reason for this interactive thing...

Mark Presling added a comment - 01/Jun/06 11:03 PM
When I said "I don't really understand why you would always want to return null", I wasn't bagging you... I was just meaning "why IT would always return null". ;)

Thanks again for your help.

Jesse Eichar added a comment - 02/Jun/06 11:27 AM
The interactive bit is so that developers can get images asynchronously. Although I wonder why the renderer is doing that... I think that maybe we should be changing the renderer at this point.

Jesse Eichar added a comment - 02/Jun/06 04:18 PM
I've looked closer. I think the return null bit is so that the image will only be attempted 1 time. If the load fails then it will always return null... See kind of dumb though. Seems like it should return an ERROR object or something.

I see the problem now... I think its fixed now.