0

I am using Spring Boot 1.4, Spock and spring-boot-starter-security. Here is my relevant gradle file:

springBootVersion = '1.4.0.BUILD-SNAPSHOT'
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-devtools')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.jasypt:jasypt-spring31:1.9.2')
compile('org.apache.httpcomponents:httpclient:4.5.2')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.spockframework:spock-core:1.1-groovy-2.4-rc-2')
testCompile('org.spockframework:spock-spring:1.1-groovy-2.4-rc-2')

I also have added this to my application-test.properties file:

server.ssl.enabled=false
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver

I was able to create an authenticated endpoint that creates a JWT (using jasypt-spring31:1.9.2) and returns it in the Authorization header. This works great when accessing via browser or Postman. However, I have not been able to make this work in my Spring Boot Integration Test.

I do have the DB Configuration class since my password is encrypted:

@Configuration
public class DBConfig {

    @Autowired
    private Environment env;

    @Value("${spring.datasource.driverClassName}")
    private String databaseDriverClassName;

    @Value("${spring.datasource.url}")
    private String datasourceUrl;

    @Value("${spring.datasource.username}")
    private String databaseUsername;

    @Value("${jasypt.key}")
    private String jasyptKey;

    @Bean
    public DataSource datasource() throws IOException {

        return DataSourceBuilder
                        .create()
                        .username(databaseUsername)
                        .password(getSecurePassword())
                        .url(datasourceUrl)
                        .driverClassName(databaseDriverClassName)
                        .build();
    }

    private String getSecurePassword() throws IOException {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(jasyptKey);
        Properties props = new EncryptableProperties(encryptor);
        props.load(this.getClass().getClassLoader().getResourceAsStream("application-" + env.getActiveProfiles()[0] + ".properties"));
        return props.getProperty("spring.datasource.password");
    }
}

My base test class will start up an embedded Tomcat instance and make all endpoints available, and has a method to get the JWT:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource("classpath:application-test.properties")
class BaseSpecification extends Specification {

    @Autowired
    def TestRestTemplate restTemplate

    String loginAndGetJWT() {

        def model = new ModelMap();
        model.addAttribute("username", "ausername")
        model.addAttribute("password", "apassword");

        ResponseEntity<JwtAuthenticationResponse> response = restTemplate.getForEntity("/auth", String.class, model);
        def jwt = response.getBody().token

        return jwt;
    }
}

When I try to run my sample test:

class ApplicationSpecWithoutAnnotation extends BaseSpecification {

    @Autowired
    WebApplicationContext context

    def "should boot up without errors"() {

        expect: "web application context exists"
        context != null
    }
}

I get the following:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testRestTemplate': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.apache.http.conn.ssl.SSLConnectionSocketFactory
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]

According to this: TestRestTemplate is now available as bean whenever @SpringBootTest is used.

If I remove compile('org.apache.httpcomponents:httpclient:4.5.2') then the error changes to:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testRestTemplate': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients

So there appears to be a dependency on Spring web & apache that doesn't seem right.

sonoerin
  • 5,015
  • 23
  • 75
  • 132
  • I think you don't need the `httpclient` as an explicit dependency, your error indicates a problem while initializing the class (see this [link](https://stackoverflow.com/questions/1457863/what-causes-and-what-are-the-differences-between-noclassdeffounderror-and-classn) for an explanation of `NoClassDefFoundError`). Which JDK version are you using? You could also try to set `server.ssl.enabled=false` to `true` or simply omit the config key. – Kevin Wittek Oct 21 '16 at 08:41
  • I actually have server.ssl.enabled=false and am using JDK1.8. When I remove the line "compile('org.apache.httpcomponents:httpclient:4.5.2')" from my build.gradle I still get the error: Caused by: java.lang.NoClassDefFoundError: org/apache/http/impl/client/HttpClients. I wonder if this is a gradle issue because all the spring examples that I based this off of are in maven? – sonoerin Oct 23 '16 at 21:15
  • I don't think this is a Gradle issue. Can you publish a small example project demonstrating your problem on Github? – Kevin Wittek Oct 27 '16 at 21:45
  • Sorry Kevin, not sure how to break this out without violating my NDA. – sonoerin Nov 06 '16 at 19:41

1 Answers1

1

turns out that my included QBO libraries had the offending http library. I removed all references to that 2.5 library and my build was fixed. Not sure why it didn't show up in my gradle dependency tree, but when I looked at the imported jars I found it.

sonoerin
  • 5,015
  • 23
  • 75
  • 132