java.lang.NoSuchMethodError when the method definitely exists
Asked Answered
M

3

8

I have a Spring framework based Java web application, which has been built in SpringSource Tool Suite ("STS"), and a local copy of Apache Tomcat. We also have a internal production server, again running Tomcat.

When I run the application on my development machine, and carry out a specific action in the web application, everything works correctly. However, when I deploy the web application to Tomcat on the server (via a war file produced by maven), and repeat those aforementioned specific actions, I'm presented with some unexpected behaviour. When I checked the server tomcat log file, I found this...

2011-11-16 19:36:45,090 [http-8280-Processor1] ERROR [attachments]  invoke - Servlet.service() for servlet attachments threw exception java.lang.NoSuchMethodError: net.wmfs.coalesce.aa.dao.MediaDao.updateAlfrescoNodeRef(Ljava/lang/Long;Ljava/lang/String;)V
at net.wmfs.coalesce.aa.service.impl.MediaServiceImpl.doFileUpload(MediaServiceImpl.java:102)
at net.wmfs.coalesce.aa.servlet.MediaServlet.doFileUpload(MediaServlet.java:83)
at net.wmfs.coalesce.aa.servlet.MediaServlet.doPost(MediaServlet.java:55)

Now, the updateAlfrescoNodeRef method definitly exists in the MediaDao class - otherwise my code would not compile in STS...

package net.wmfs.coalesce.aa.dao;

public class MediaDao extends JdbcDaoSupport {

    public void updateAlfrescoNodeRef(final Long recordId, final String nodeRef) {
        // java code
    }
}

As you can see, the method signature is correct.

I suspected that there may have been a problem when maven produced the war file, so I extracted the war files contents. In the WEB-INF/lib folder, I found the jar file which holds the MediaDao class, and extracted its contents. I then did a...

cat ./MediaDao.class

Now, as class files are binary files, I mostly saw gobledegook. However, I was able to clearly make out references to the updateAlfrescoNodeRef method, as well as the contents of a String in that method. So, this means that the method is definitely there.

The bean configuration in the Spring framework XML files is definitely correct, or the code would not run when I execute it on my development machine.

Googling suggested a library conflict on the server, but all the referenced classes - MediaServlet, MediaServiceImpl, MediaDao - are in the main project (the one with the WEB-INF folder in it). While its conceivable there may be multiple copies of the dependencies on the server, there is definitely only one copy of the main project jar.

Does anyone have any ideas why this is happening?

Monodrama answered 17/11, 2011 at 13:42 Comment(10)
Once you have the jar file extracted you can do javap -classpath <your-jar-file> net.wmfs.coalesce.aa.dao.MediaDao and it should list the method signatures. docsModal
compare the size and date of this class (MediaBlablahblah.class) on developer machine and on the serverCapparidaceous
@MikeSamuel, relevant javap output follows... Compiled from "MediaDao.java" public class net.wmfs.coalesce.aa.dao.MediaDao extends org.springframework.jdbc.core.support.JdbcDaoSupport{ public void updateAlfrescoNodeRef(java.lang.Long, java.lang.String); }Monodrama
can you share your code which referes to this line: MediaServiceImpl.doFileUpload(MediaServiceImpl.java:102). Also the declaration of MediaDao, whether it is MediaDao mediaDao or JdbcDaoSupport MediaDao?Bautzen
@Capparidaceous Surprisingly, the MediaDao.class on my development machine (produced by STS) is 5111 bytes, while the one on my server is 5092 bytes! Although, the file i'm comparing on my dev machine is produced by STS, as opposed to maven. When I performed a maven package, the MediaDao.class it produced was also 5092 bytes.Monodrama
@Bautzen The MediaServiceImpl class has a private property named mediaDao of type MediaDao. The class has a standard setter method so that Spring can inject the dependency using the bean definition xml files. Those files have a <bean id="mediaDao" class="net.wmfs.coalesce.aa.dao.MediaDao"> (with the required properties). It also has a <bean id="mediaProcessor" class="net.wmfs.coalesce.aa.service.impl.MediaServiceImpl">, which has a <property name="mediaDao" ref="mediaDao"/> line. The method calling line is mediaDao.updateAlfrescoNodeRef(upload.getId(), nodeRef);Monodrama
@Bautzen The method calling line - mediaDao.updateAlfrescoNodeRef(upload.getId(), nodeRef); passes two parameters. The upload.getId() method returns a java.lang.Long, and nodeRef is a java.lang.String.Monodrama
@Monodrama as you understand they should be equal if you are expecting the equal behaviour. Just investigate why the maven produces different comparing to those which produced by 'STS'.Capparidaceous
then I would ask you, try using decompiler: java.decompiler.free.fr. Its a nice tool. This can be my last suggestion.Bautzen
@Bautzen I downloaded the tool you mentioned and decompiled the MediaDao.class files produced by STS and maven. They both have the public void updateAlfrescoNodeRef(Long recordId, String nodeRef) method. Thanks for your efforts Kowser.Monodrama
M
12

The problem has now been resolved. Thank you everyone for your assistance.

It turns out that the main project had a dependency which had another MediaDao class, in exactly the same package path. Someone had basically copied the class into that dependency (as a library resource so that lots of projects could use it without specifying the main project as a dependency). However, that someone had not removed the class in the main project.

So, when I modified the class in the main project (I added the updateAlfrescoNodeRef method), and ran the application in STS on my machine, Tomcat used the version of the class in the main project, and not in the library because the library project was closed. When the application was deployed to the server however, it looks like the version of the class in the library was used instead (which, of course, didn't have the updateAlfrescoNodeRef method in it).

Expert tip if you ever find yourself in a similar situation: In STS, press CTRL+SHIFT+T to open the "Open Type" dialog, and enter the name of the problematic class to see a list of projects that have a class with that name.

Monodrama answered 18/11, 2011 at 12:25 Comment(1)
thank you, your comments really help me. In my project, i use a maven plugin to generate Java pojo classes based on json schema. And by mistake, i create the same java class in the same package with the will-be-generated-class. I still don't understand why it causes "java.lang.NoSuchMethodError" in production only, even production, testing, or development use same docker imageHester
P
2

If the error occured in android studio, it also can be a bug of the Instant Run. In that case: File -> Invalidate Caches/Restart. It solved my problem

Pinole answered 9/11, 2016 at 13:48 Comment(1)
ugh. 3 years of android development experience and I still forget to try this.Whitesell
L
0

If you are using Tomcat 6+, look in ~tomcat/lib for conflicting classes and jars. In Tomcat 5, look in ~tomcat/common/classes, ~tomcat/common/lib, ~tomcat/shared/classes and ~tomcat/shared/lib.

Lakeishalakeland answered 17/11, 2011 at 15:40 Comment(3)
We are using Apache Tomcat version 5.5.30 (on the server), and version 5.5.33 (on the development machine). We are using Java version 1.6.0_0 (on the server), and version 1.6.0_26 (on the development machine). We are using the Spring Framework version 3.0.2.Monodrama
There are various libraries in ~tomcat/common/lib. All but 2 are third party libraries. Those two do not contain the classes in question (I downloaded them, decompressed them and checked).Monodrama
This worked for me. Thanks a lot ! Invested 2 hrs into this ! A simple clean server worked wonders for me. Now to understand why.Revest

© 2022 - 2024 — McMap. All rights reserved.