1

I developed a Spring Boot WebApplication that receives a soap request with some data and save it to database. As I am a kind of beginner on Spring Boot and JPA, I used a commonly found configuration for JPA where the primary key is an Id managed by JPA.

@Table(
        name="MyEntity",
        uniqueConstraints=@UniqueConstraint(columnNames=["channel", "service"])
)
public class MyEntity {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Long id
    String channel
    String service

I developed a set of Spock test about saving, searching and modifying entity and everything runs smoothly. Specifically I have a Spring Boot application smoke test to be sure that configuration load correctly

@ContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationContextSpec extends Specification {

    @Autowired
    WebApplicationContext context

    def "should boot up without errors"() {
        expect: "(web) application context exists"
        context != null
    }
} 

As the natural key is channel and service, I tried to define a composite key

@Table(name="MyEntity")
@IdClass(MyEntityPk.class)
public class MyEntity {

    @Id
    String channel
    @Id
    String service

along with the primary key class (excerpt)

public class MyEntityPk implements Serializable {
    String channel
    String service



@ContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApplicationContextSpec extends Specification {

    @Autowired
    WebApplicationContext context

    def "should boot up without errors"() {
        expect: "(web) application context exists"
        context != null
    }
}

After this change, Spring Boot application smoke test fails with

java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
...
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.

along with all other jpa related tests. As a further information, I picked Spring Boot gs-accessing-data-jpa-master sample and adapter it to use gradle and spock. Then I modified to use a composite key and Spring Boot application smoke test is successful as expected. On my Ws Web Application (uses dependency spring-boot-starter-web-services) there is something that causes failure. I would like to point out that the only changes were moving from auto generated key to composite key. This causes failure to find EmbeddedServletContainerFactory. Do you have any suggestion on the matter ?

I am posting also che build script, quite complex so I added some comments to clarify implementation

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'

// I am using a mixed environment with some java generated source code
// and mainly groovy classes. This sourceSets definition forces to compile
// everything with groovy compiler so that java classes see groovy classes
sourceSets {
    main {
        groovy {
            srcDirs = ['src/main/groovy', 'src/main/java']
        }
        java {
            srcDirs = []
        }
    }

    test {
        groovy {
            srcDirs = ['src/test/groovy','src/test/java']
        }
        java {
            srcDirs = []
        }
    }
}

repositories {
    mavenCentral()
}

// This task defines a jaxb task that takes xsd schema definition
// and generates java classes used when mapping soap requests
task genJaxb {
    ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
    ext.classesDir = "${buildDir}/classes/jaxb"
    ext.schema = "src/main/resources/myws.xsd"

    outputs.dir classesDir

    doLast() {
        project.ant {
            taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
                    classpath: configurations.jaxb.asPath
            mkdir(dir: sourcesDir)
            mkdir(dir: classesDir)

            xjc(destdir: sourcesDir, schema: schema) {
                arg(value: "-wsdl")
                produces(dir: sourcesDir, includes: "**/*.java")
            }

            javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true,
                    debugLevel: "lines,vars,source",
                    classpath: configurations.jaxb.asPath) {
                src(path: sourcesDir)
                include(name: "**/*.java")
                include(name: "*.java")
            }

            copy(todir: classesDir) {
                fileset(dir: sourcesDir, erroronmissingdir: false) {
                    exclude(name: "**/*.java")
                }
            }
        }
    }
}

configurations {
    jaxb
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-web-services")
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile("org.codehaus.groovy:groovy-all:2.4.7")
    compile("wsdl4j:wsdl4j:1.6.1")
    compile(files(genJaxb.classesDir).builtBy(genJaxb))
    runtime('com.h2database:h2')
    jaxb("org.glassfish.jaxb:jaxb-xjc:2.2.11")
    testCompile 'org.springframework.boot:spring-boot-starter-test'
    testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'
    testCompile 'cglib:cglib-nodep:3.2.5'
}


jar {
    baseName = 'myws'
    version =  '0.7.1'
    excludes = ['**/application.yml']
    from sourceSets.main.output
    // adding jaxb generated file to be included in jar
    from('build/classes/jaxb') {
        include '**'
    }
}

// this defines a uber-jar so that I may launch spring server 
// from command line using java -jar myws-spring-boot.jar
task('execJar', type:Jar, dependsOn: 'jar') {
    baseName = 'myws'
    version =  '0.7.1'
    classifier = 'spring-boot'
    from sourceSets.main.output
    from('build/classes/jaxb') {
        include '**'
    }

}

bootRepackage  {
    withJarTask = tasks['execJar']
}

Eventually, all I have to do is:

gradlew build

to build the jar and run the tests and then

java -jar myws-spring-boot.jar

to launch the spring boot server

Evelino Bomitali
  • 173
  • 2
  • 13

1 Answers1

0

Smoke test was not working because I did not perform all required changes when moving from the "Long id" field as primary key

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Long id

to

@Id
String channel
@Id
String service

I forgot to remove a method in the repository class that was referencing the "Long id" field. After moving to spring-spock 1.1 following Leonard Brünings' suggestion, I got a more detailed error message that pointed me to the right direction.

Error creating bean with name 'modelEntryRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property id found for type ModelEntryEntity!

So I removed the references (method declaration in repository interface) and smoke test ended successfully. By the way, within build.gradle I had to add spock core explicitly, so instead of one line with reference to spring-spock 1.0 I had to put

testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
testCompile 'org.spockframework:spock-spring:1.1-groovy-2.4'
Evelino Bomitali
  • 173
  • 2
  • 13