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?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Jazz
  • 307
  • 1
  • 2
  • 20
  • 2
    Once you have the jar file extracted you can do `javap -classpath net.wmfs.coalesce.aa.dao.MediaDao` and it should list the method signatures. [docs](http://download.oracle.com/javase/1,5.0/docs/tooldocs/windows/javap.html) – Mike Samuel Nov 17 '11 at 13:48
  • compare the size and date of this class (MediaBlablahblah.class) on developer machine and on the server – javagirl Nov 17 '11 at 13:49
  • @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); } – Jazz Nov 17 '11 at 13:58
  • 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`? – Kowser Nov 17 '11 at 13:59
  • @javagirl 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. – Jazz Nov 17 '11 at 14:14
  • @Kowser 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 `` (with the required properties). It also has a ``, which has a `` line. The method calling line is `mediaDao.updateAlfrescoNodeRef(upload.getId(), nodeRef);` – Jazz Nov 17 '11 at 14:25
  • @Kowser 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. – Jazz Nov 17 '11 at 14:29
  • @Jazz 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'. – javagirl Nov 17 '11 at 14:34
  • then I would ask you, try using decompiler: http://java.decompiler.free.fr/. Its a nice tool. This can be my last suggestion. – Kowser Nov 17 '11 at 14:51
  • 1
    @Kowser 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. – Jazz Nov 17 '11 at 15:06

3 Answers3

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.

Jazz
  • 307
  • 1
  • 2
  • 20
  • 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 image – anhquan Oct 18 '18 at 21:06
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

Beks
  • 363
  • 2
  • 12
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.

z12345
  • 2,186
  • 4
  • 20
  • 28
  • 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. – Jazz Nov 17 '11 at 15:59
  • 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). – Jazz Nov 17 '11 at 16:08
  • This worked for me. Thanks a lot ! Invested 2 hrs into this ! A simple clean server worked wonders for me. Now to understand why. – Ashwin Jun 06 '17 at 05:45