Application context initialization errors with an UnsatisfiedDependencyException when an inner class configured bean is encountered while using Kotlin and Spring Boot. The reported cause is a BeanCreationException due to an ArrayIndexOutOfBoundsException.

Below is the stack trace:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
Oct 29, 2019 10:14:43 PM org.springframework.boot.SpringApplication reportFailure
SEVERE: Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myBeanName': Unsatisfied dependency expressed through field 'restAuthenticationEntryPoint'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBeanName': Unexpected exception during bean creation; nested exception is java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
    at io.github.kwahome.SampleKotlinApplicationKt.main(SampleKotlinApplication.kt:13)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myBeanName': Unexpected exception during bean creation; nested exception is java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:528)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1171)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 24 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:705)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    ... 32 more

Reproducer:

import java.time.OffsetDateTime
import javax.servlet.ServletException
import java.io.IOException
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpServletRequest

import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.security.servlet.WebSecurityEnablerConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter
import org.springframework.stereotype.Component

@Configuration
@EnableWebSecurity
class ApiSecurityConfig : WebSecurityEnablerConfiguration() {

    @Value("\${http.auth-token-header-name}")
    private val principalRequestHeader: String? = null

    @Value("#{'\${http.auth-token}'.split(',')}")
    private val principalRequestValues: List<String>? = null

    @Autowired
    private val restAuthenticationEntryPoint: RestAuthenticationEntryPoint? = null

    fun apiAuthenticationFilter(): ApiKeyAuthFilter? {
        val filter = principalRequestHeader?.let { ApiKeyAuthFilter(it) }
        filter?.setAuthenticationManager { authentication ->
            val principal = authentication.principal as String
            val validPrincipal = principalRequestValues!!.stream()
                .anyMatch { principalRequestValue -> principalRequestValue.trim { it <= ' ' } == principal }
            if (!validPrincipal) {
                // not made a constant as this value gets converted by to a message that adheres to API standards
                // in the RestAuthenticationEntryPoint below.
                throw BadCredentialsException("Invalid API Key received.")
            }
            authentication.isAuthenticated = true
            authentication
        }
        return filter
    }

    @Throws(Exception::class)
   fun configure(httpSecurity: HttpSecurity) {
        httpSecurity.cors()
        httpSecurity
            .antMatcher("**")
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().addFilter(apiAuthenticationFilter()).authorizeRequests()
            .anyRequest().authenticated().and()
            .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)
    }

    @Component
    inner class RestAuthenticationEntryPoint : AuthenticationEntryPoint {

        @Autowired
        private val objectMapper: ObjectMapper? = null

        @Throws(IOException::class, ServletException::class)
        override fun commence(
            httpServletRequest: HttpServletRequest,
            httpServletResponse: HttpServletResponse,
            exception: AuthenticationException
        ) {
            val response = "Unauthorized"
            httpServletResponse.contentType = "application/json"
            httpServletResponse.status = HttpStatus.UNAUTHORIZED.value()

            val out = httpServletResponse.outputStream
            objectMapper!!.writeValue(out, response)
            out.flush()
        }
    }

    inner class ApiKeyAuthFilter(private val principalRequestHeader: String) :
        AbstractPreAuthenticatedProcessingFilter() {

        override fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Any {
            return request.getHeader(principalRequestHeader).orEmpty()
        }

        override fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any {
            return "N/A"
        }
    }
}

Comment From: mbhave

The code in question is in Spring Framework. There is an existing Spring Framework issue related to this.

Comment From: fast-reflexes

How come @Configurationworks on inner classes but not @Component? I thought a @Configuration was also a @Component: https://stackoverflow.com/questions/39174669/what-is-the-difference-between-configuration-and-component-in-spring

Comment From: wilkinsona

@fast-reflexes It doesn't work with @Component as it's a feature that is specific to @Configuration classes. If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.