spring-mvcspring-bootspring-batchspring-batch-admin

Spring Batch Admin + Spring Boot - Ambiguous mapping. Cannot map 'org.springframework.batch.admin.web.JobController#1' method


I am trying to have Spring Batch Admin UI using spring-batch-admin-samples project.

My job uses Spring Boot and I have changed my main application class to be deployable to Weblogic using this link.

So application class looks like,

@SpringBootApplication(exclude = { HypermediaAutoConfiguration.class,
        MultipartAutoConfiguration.class })
@EnableBatchAdmin
public class MyApplication extends SpringBootServletInitializer
        implements WebApplicationInitializer {

    @Override
    protected SpringApplicationBuilder configure(
            SpringApplicationBuilder builder) {
        return configureApplication(builder);
    }

    public static void main(String[] args) {
        configureApplication(new SpringApplicationBuilder()).run(args);
    }

    private static SpringApplicationBuilder configureApplication(
            SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF);
    }
}

I have removed - @EnableBatchProcessing from configuration class too.

I received quite a few errors till now ( more than 10 ) and was able to get past each by overriding xmls etc or by adding properties.

This below error is what I am not able to fix,

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'org.springframework.batch.admin.web.JobController#1' method 
public java.lang.String org.springframework.batch.admin.web.JobController.launch(org.springframework.ui.ModelMap,java.lang.String,org.springframework.batch.admin.web.LaunchRequest,org.springframework.validation.Errors,java.lang.String)
to {[/jobs/{jobName}],methods=[POST]}: There is already 'org.springframework.batch.admin.web.JobController#0' bean method
public java.lang.String org.springframework.batch.admin.web.JobController.launch(org.springframework.ui.ModelMap,java.lang.String,org.springframework.batch.admin.web.LaunchRequest,org.springframework.validation.Errors,java.lang.String) mapped.
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:369)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:313)
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:134)
    at com.hms.responsematching.batch.ResponseMatchingApplication.main(ResponseMatchingApplication.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
    at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59)
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'org.springframework.batch.admin.web.JobController#1' method 
public java.lang.String org.springframework.batch.admin.web.JobController.launch(org.springframework.ui.ModelMap,java.lang.String,org.springframework.batch.admin.web.LaunchRequest,org.springframework.validation.Errors,java.lang.String)
to {[/jobs/{jobName}],methods=[POST]}: There is already 'org.springframework.batch.admin.web.JobController#0' bean method
public java.lang.String org.springframework.batch.admin.web.JobController.launch(org.springframework.ui.ModelMap,java.lang.String,org.springframework.batch.admin.web.LaunchRequest,org.springframework.validation.Errors,java.lang.String) mapped.
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:250)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:214)
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:184)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:128)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
    ... 23 common frames omitted

I can think of two issues ,

1.org.springframework.batch.admin.web.JobController has two methods with same URL but different HTTP methods - launch & details.

<dependency>
        <groupId>org.springframework.batch</groupId>
        <artifactId>spring-batch-admin-manager</artifactId>
        <version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
  1. web.xml in spring-batch-admin-resources project uses contextConfigLocation from two xmls so there might be a context loading issue.

    <context-param><param-name>contextConfigLocation</param-name> <param-value>classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml</param-value> </context-param>

and

<servlet>
        <servlet-name>Batch Servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:/org/springframework/batch/admin/web/resources/servlet-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

Please suggest and let me know if any more details needed.

EDIT: By looking at accepted answer of this question, it seems that same URL can be applied to different HTTP methods and that would not produce error. Cause is somewhere else.

I just don't understand that how come everything works OK when running jar of spring-batch-admin-samples project. but start receiving issues when try on own similar code. Application class is same as mentioned in samples. You start receiving issue mapping issue when try to fix below issue about object mappers by overriding these two below mentioned xmls.

Constructor in org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration required a single bean, but 2 were found:
    - jacksonMapper: defined in URL [jar:file:/D:/RunRC/ResponseMatching/ResponseMatchingWithAdmin-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-batch-admin-resources-2.0.0.BUILD-SNAPSHOT.jar!/META-INF/spring/batch/servlet/resources/resources-context.xml]
    - objectMapper: defined in URL [jar:file:/D:/RunRC/ResponseMatching/ResponseMatchingWithAdmin-0.0.1-SNAPSHOT.jar!/BOOT-INF/lib/spring-batch-admin-manager-2.0.0.BUILD-SNAPSHOT.jar!/META-INF/spring/batch/servlet/manager/controller-context-legacy.xml]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Start receiving above issue when done with fixing Proxy Issue on SimpleJobService

Overriding controller-context-legacy.xml with no entries / empty file or a single entry like , <bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" primary="true"/> has no impact.

All in all, I simply want to run said sample project with my job. One difference being that sample project used Gradle while I use Maven to build my project. My job was already written using Maven so continued with that.


Solution

  • After few days of hit and trial, I am able to get that Sample Github project working for my job. Two root causes,

    1.@Configuration & @SpringBootApplication being in same package for my job: My context was getting initialized twice because I had both classes in same package so I had to move configuration class to a different package than main class.

    2.Spring Boot Version: Sample project is coded in Spring Boot Version 1.2.2.RELEASE while My job was using 1.4.0.RELEASE and that was causing objectMapper issues. Project worked with 1.2.2.RELEASE and also with latest version, 1.5.2.RELEASE so I upgraded myself to 1.5.2.RELEASE.

    I faced various other issues with,

    and my deployment environment was Weblogic and RDBMS being DB2. I was able to solve all these issues mostly my overriding XMLs of spring-batch-admin-manager API.

    enter image description here

    Hope this helps to anybody going to use that sample project as starting point.