arcseldon
arcseldon

Reputation: 37135

Upgrading Spring Security 2.5 to 3.1 - java.lang.NoClassDefFoundError: org/springframework/security/Authentication

This problem is proving very tricky to resolve. Usual story, inherited a very poorly maintained java web application with no unit tests and a variety of ancient jar dependencies with no version info dumped in a lib directory built using Ant. In order to get better maintenance and understanding of the dependencies I migrated over to Maven. Subsequently, I realised the version of Spring was quite old (Spring 2.x and Spring Security 2.0.3). I upgraded successfully to Spring 2.5. I have now started migrating Spring up to 3.1.2.RELEASE, and Spring Security up to 3.1.3.RELEASE.

Everything compiles, I don't get any namespace issues either (declared in headers of Spring XML configs), but fails when being deployed as a WAR file into Tomcat Container. The log file reports:

Could not instantiate bean class [com.mydomain.repository.ReportDaoImpl]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/security/Authentication
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:997)

I checked, and org.springframework.security.Authentication belongs to the old Spring Security jar (2.0.4)

My current Maven dependencies are as follows:

<spring.version>3.1.2.RELEASE</spring.version>

      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!--  SPRING SECURITY -->

             <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-acl</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-crypto</artifactId>
        <version>3.1.3.RELEASE</version>
    </dependency>

My security.xml is minimal:

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<http auto-config='true'>
    <intercept-url pattern="/**" access="ROLE_Admin" />
</http>

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="admin" password="password" authorities="ROLE_Admin" />
        </user-service>
    </authentication-provider>
</authentication-manager>        

All my other Spring Config files use the following namespace headers in their configs:

<?xml version="1.0" encoding="UTF-8"?>

 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

As far as I know this application contains NO annotations regarding Spring.

So how does the Spring Security 2.0.4 class org.springframework.security.Authentication get requested by a Spring 3.1 class org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory when instantiating a DAO object (which has nothing to do with Spring security settings). It may be that the DAO is picked because it is the first bean in order to get instantiated in the applicationContext.xml by the classloader (via Spring dependency injection container) but i cannot see how there is still a reference lurking somewhere in this application to an old 2.0.4 class. Since everything compiles ok, and the Maven pom only references 3.1 my take is that there must be some configuration somewhere still trying to pull in an old class. Anyone with Spring Security knowledge (in particular upgrading a large app from a version 2 to version 3) might have hit this problem before but I couldn't find exact match via google. Thanks for any suggestions on this. Currently stumped.

Quick update:

The applicationContext.xml has the namespace header as given above and the DAO is simply referenced as follows:

<bean id="reportDao" class="com.mydomain.repository.ReportDaoImpl">
    <property name="dataSource" ref="myDataSource" />
</bean>

There is really nothing more to it. The applicationContext pulls in another context file applicationContext-datasource which declares (same namespace header again as above):

 <bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
</bean>

<bean id="myDataSource" parent="parentDataSource">
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

The ReportDao is over 1500 lines of poorly written procedural code. It is a POJO and implements rg.springframework.context.ApplicationContextAware. It also uses org.springframework.jdbc.core.support.JdbcDaoSupport for performing CRUD operations to the database.

Running mvn -X the output for dependencies (have switched to Spring [main] 3.0.7):

   org.springframework:spring-webmvc:jar:3.0.7.RELEASE:compile
    org.springframework:spring-asm:jar:3.0.7.RELEASE:compile
    org.springframework:spring-beans:jar:3.0.7.RELEASE:compile
    org.springframework:spring-expression:jar:3.0.7.RELEASE:compile
    org.springframework:spring-web:jar:3.0.7.RELEASE:compile
 org.springframework:spring-aop:jar:3.0.7.RELEASE:compile
    aopalliance:aopalliance:jar:1.0:compile
 org.springframework:spring-context:jar:3.0.7.RELEASE:compile
 org.springframework:spring-context-support:jar:3.0.7.RELEASE:compile
 org.springframework:spring-core:jar:3.0.7.RELEASE:compile
 org.springframework:spring-tx:jar:3.0.7.RELEASE:compile
 org.springframework:spring-jdbc:jar:3.0.7.RELEASE:compile
 org.springframework.security:spring-security-acl:jar:3.1.3.RELEASE:compile
 org.springframework.security:spring-security-config:jar:3.1.3.RELEASE:compile
 org.springframework.security:spring-security-core:jar:3.1.3.RELEASE:compile
 org.springframework.security:spring-security-taglibs:jar:3.1.3.RELEASE:compile
 org.springframework.security:spring-security-web:jar:3.1.3.RELEASE:compile
 org.springframework.security:spring-security-crypto:jar:3.1.3.RELEASE:compile

Looks ok I believe.

The only spring jars (grepped to be certain) in the WEB-INF/lib directory of the deliverable WAR file are:

./spring-aop-3.0.7.RELEASE.jar
./spring-asm-3.0.7.RELEASE.jar
./spring-beans-3.0.7.RELEASE.jar
./spring-context-3.0.7.RELEASE.jar
./spring-context-support-3.0.7.RELEASE.jar
./spring-core-3.0.7.RELEASE.jar
./spring-expression-3.0.7.RELEASE.jar
./spring-jdbc-3.0.7.RELEASE.jar
./spring-security-acl-3.1.3.RELEASE.jar
./spring-security-config-3.1.3.RELEASE.jar
./spring-security-core-3.1.3.RELEASE.jar
./spring-security-crypto-3.1.3.RELEASE.jar
./spring-security-taglibs-3.1.3.RELEASE.jar
./spring-security-web-3.1.3.RELEASE.jar
./spring-tx-3.0.7.RELEASE.jar
./spring-web-3.0.7.RELEASE.jar
./spring-webmvc-3.0.7.RELEASE.jar

Again, looks sensible.

Grepping the source code for "Authentication" did not help. This looks like a transitive runtime dependency issue. It goes unnoticed at compilation and isn't declared anywhere as a first level dependency. But somehow at runtime (in the Tomcat 6 container) when deployed a rogue reference to an old library file is requested.

Going to delete my Tomcat instance and start from scratch just as a precaution.

Upvotes: 0

Views: 8896

Answers (1)

arcseldon
arcseldon

Reputation: 37135

Ok, finally solved this particular problem. NoClassDefFoundError is exactly what it says on the tin. As Luke Taylor asked "Are you absolutely sure you recompiled?" Well, yes and no. I definitely recompiled, cleaned target (using Maven) etc. And the first time I used a decompiler to see the source for the generated class "XXXDao" I saw my changes. But I also noticed that when I added / removed a couple of lines from the java source, the error in the stacktrace remained on the same (random) line number. That explained I was getting a stale .class file somehow. Turns out, Maven and / or Eclipse (m2eclipse plugin) somehow managed to compile class files into the WEB-INF/classes directory of my core project (not target) and was non-deterministically overwriting the WEB-INF/classes directory in target with some classes. Even stranger, it ALWAYS deployed the old classes when compressing the classes into .WAR. So you have a case of the exploded contents looking correct but actually the compresssed WAR file with different classes.

Lesson learnt - if you get this sort of behaviour, double check the compilation steps, and try to find out how perhaps an old class file (referencing the old Authentication class) has gotten into your new build. Thanks for the pointers from those that contributed to the post!

Upvotes: 2

Related Questions