Error using IMAGE/IO in a Servlet

476 views
Skip to first unread message

Sean

unread,
Oct 28, 2013, 3:32:42 PM10/28/13
to google-we...@googlegroups.com
I'm trying to read a .tiff on the back end of my server. I do:

[...]
FileImageInputStream fis = new FileImageInputStream(new File(p_tifName));
Object o = ImageIO.getImageReadersByFormatName(
"tiff").next();
TIFFImageReader reader = (TIFFImageReader)o;
reader.setInput(fis);
[...]

If I run this code in the Servlet via a main() it works fine. But when running in Dev Mode, I get the error:
java.lang.ClassCastException: com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader cannot be cast to com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader

I have the imageIO.jar on the class path. Everything seems happy, except in Dev mode. Does anyone have any ideas? I'm at a loss at how to get around an error saying a class can't be casted to itself. 

-Sean

Sean

unread,
Oct 28, 2013, 3:34:54 PM10/28/13
to google-we...@googlegroups.com
This is the full Error:
java.lang.ClassCastException: com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader cannot be cast to com.sun.media.imageioimpl.plugins.tiff.TIFFImageReader
at com.ll.io.GeoImageReader.readTif(GeoImageReader.java:54)
at com.ll.cidb.server.MapsServlet.doGet(MapsServlet.java:76)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:829)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:513)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)

Jens

unread,
Oct 28, 2013, 4:17:13 PM10/28/13
to google-we...@googlegroups.com
This kind of ClassCastException typically occurs if you have a ClassLoader issue. If the same class is loaded by two different ClassLoader then Java treats these two classes as different and its likely that you get the above ClassCastException.

First I would make sure that you have the imageIO.jar only once in your class path and then you could check if you have a ClassLoader leak when you redeploy your application. 

A ClassLoader leak isn't that unlikely because the first call to ImageIO pins the current ClassLoader. If that's the WebAppClassLoader that is responsible for your app, the app server (jetty) can not garbage collect your deployed app once you redeploy it because some Java system classes will hold a reference to that WebAppClassLoader...well and this reference will never go away unless you restart the server. 
If you are in that situations then its likely that everything works the first time you start Jetty and it will start to fail once you have redeployed your app the first time. 

To fix this situation you could try using a ServletContextListener and in its contextInitialized() method you first change the class loader of the current thread to the system class loader. Then you make a dummy call to ImageIO and finally you set back the class loader to the original one.

In our app we have to do that for multiple classes because libraries (and Java) are sometimes written in a way thats not very compatible to application server class loading.

-- J.




Thad Humphries

unread,
Oct 28, 2013, 4:47:27 PM10/28/13
to google-we...@googlegroups.com
The problem sounds like the JreMemoryLeakPreventionListener. In $CATALINA_HOME/conf/server.xml add appContextProtection="false" to that Listener:

<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"
    appContextProtection="false" />

You will be opening up Tomcat to a memory leak, which will require restarting Tomcat when you change your WAR file.

For more on this issue, see this Tomcat thread: http://bit.ly/1bwiKuM and the Tomcat list for more info. 

On Monday, October 28, 2013 3:32:42 PM UTC-4, Sean wrote:

Sean

unread,
Oct 29, 2013, 7:29:49 AM10/29/13
to google-we...@googlegroups.com
Wow, 

Thank you Jens. I would have never figured that out in a million years! That was super subtle, but I learned a lot from it!

Thank you! Your suggestion worked perfectly!

-Sean

Sean

unread,
Nov 18, 2013, 1:17:54 PM11/18/13
to google-we...@googlegroups.com
Hey guys, so I'm pulling this thread back up because I ran into a new problem. Everything has been going great, till I tried using RPC's with the context servlet changed. Now when I try to do any RPC I get the error:

Unable to get value This application is out of date, please click the refresh button on your browser. ( Blocked attempt to access interface 'com.ll.cidb.client.GreetingService', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt )

My crrent ServletContextListener does this:
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
ClassLoader imageIOLoader = TIFFImageReader.class.getClassLoader();
Launcher launcher = sun.misc.Launcher.getLauncher();
ClassLoader sunClassLoader = launcher.getClassLoader();
Thread.currentThread().setContextClassLoader(sunClassLoader);
System.out.println("Context Loader Switched.");
//Dummy class to ImageIO
javax.imageio.ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
ClassLoader tifClass = reader.getClass().getClassLoader();
ServletContext context = p_arg0.getServletContext();
context.setAttribute("TEST", "TEST_VALUE");

Without this, ImageIO fails like in my first post, but RPCs work fine. With this, ImageIO works fine, but RPCs fail. Any ideas?

Jens

unread,
Nov 18, 2013, 3:04:33 PM11/18/13
to google-we...@googlegroups.com
Maybe you missed copying it, but you never set tcl back. I never had the issue you are describing.

Thats how I do it normally: https://gist.github.com/jnehlmeier/7534390

-- J.

Sean Loughran

unread,
Nov 19, 2013, 1:53:06 PM11/19/13
to Google Web Toolkit
Hi Jens,

So I tried putting it back after calling the ImageIO library and that didn't help the ImageIO problem. And I just confirmed even copying and pasting your code (thank you for providing that by the way) also cause the ImageIO stuff not to work. I get my usual error.

Grrrrrrrrrrrrrrr. This stuff is pretty frustrating.

-Sean


--
You received this message because you are subscribed to a topic in the Google Groups "Google Web Toolkit" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/google-web-toolkit/QvduX97Swfg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to google-web-tool...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.

Sean Loughran

unread,
Dec 3, 2013, 1:16:26 PM12/3/13
to Google Web Toolkit
So, for completeness sake, I will let future Googlers know how I got around this problem. No matter which way I changed the Context Listener to and from the Jetty to Sun's and back again, I couldn't get the java advanced image library and RPCs working at the same time. So, I tried something a bit more drastic. 

I created a standalone java program that takes in the request of the image that needs to be loaded via a Socket and returns a proprietary VERSION of that image. The object returned doesn't need JAI. So, I no longer switch contexts but on the server side I create a Client that creates a socket connection, and requests the image and hten returns the image as before to the website. 

Since the JAI is only accessed on a standalone process that gets kicked off in the regular JVM, it works great! Super huge work around, but the good thing is it WORKS. I get my imagery and RPC functionality. I can move on. 

The best thing would to remove JAI entirely from the server, but I can't do that right now. 

Paul Bartlett

unread,
Sep 4, 2014, 5:27:31 PM9/4/14
to google-we...@googlegroups.com
After another million years I have been trying various things out and I found that if I add ImageIO.scanForPlugins(); before the calls it loads the tiff image writers from the WEB_INF/lib directory.   This is better than disabling memory leak protection.

Thad Humphries

unread,
Sep 5, 2014, 9:49:01 AM9/5/14
to google-we...@googlegroups.com
True. I have a static block in my RPC *Impl class that calls ImageIO.scanForPlugins(). It's not uncommon for someone to forget to modify the web.xml when upgrading Tomcat, and having that static block saves me some hassle. However the app will still leak if reloaded. The Tomcat administrator should still restart Tomcat at some point after replacing or reloading the WAR.

George Florentine

unread,
Aug 21, 2015, 2:00:05 AM8/21/15
to Google Web Toolkit
Wow.  this really saved me as well. Thx so much for posting. I had tried lots of things to get the tiff writers to load using the correct class loader; nothing I tried worked. But this did - saved me from having to rewrite a lot of code so just a +1 on how cool a trick this is :)
Reply all
Reply to author
Forward
0 new messages