Adrian Pop
Adrian Pop

Reputation: 225

NullPointerException when autowiring @ConfigurationProperties POJO

I am trying to map a set of properties from a .properties file in a POJO object. This is because I want to reuse the properties in a @Configuration class and also because my property files are encrypted and I do not want to store the OAuth client passwords in plain text in the configuration class.

Here's the POJO:

@ConfigurationProperties(ignoreInvalidFields = false, prefix = "oauth")
public class OAuthClient {

    private Map<String, String> webapp = new LinkedHashMap<>();
    private Map<String, String> admin = new LinkedHashMap<>();

    public Map<String, String> getWebapp() {
        return webapp;
    }

    public void setWebapp(Map<String, String> webapp) {
        this.webapp = new LinkedHashMap<>(webapp);
    }

    public Map<String, String> getAdmin() {
        return admin;
    }

    public void setAdmin(Map<String, String> admin) {
        this.admin = new LinkedHashMap<>(admin);
    }
}

Everything looks good to me till now. I am exposing OAuthClient in a bean in the following config class:

@Configuration
@EnableConfigurationProperties(OAuthClient.class)
@PropertySource("classpath:oauth_clients.properties")
public class OAuthClientsConfiguration {

    @Bean
    public OAuthClient oAuthClient() {
        return new OAuthClient();
    }
}

Here's the oauth_clients.properties file:

oauth.OAuthClient.webapp.id=client1
oauth.OAuthClient.webapp.secret=pass1
oauth.OAuthClient.admin.id=client2
oauth.OAuthClient.admin.secret=pass2

And finally, I want to use the properties in my custom AuthorizationServerConfigurerAdapter, but the app always fails to start with a NPE when getting the properties from the auwotired OAuthClient.

Here's the AuthorizationServerConfigurerAdapter:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private OAuthClient oAuthClient;

    ...

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(oAuthClient.getWebapp().get("id"))
                .secret(passwordEncoder.encode(oAuthClient.getWebapp().get("secret")))
                .authorizedGrantTypes(PASSWORD_GRANT, REFRESH_TOKEN_GRANT, AUTHORIZATION_CODE_GRANT)
                .scopes(READ_SCOPE, WRITE_SCOPE)
                .autoApprove(true)
                .accessTokenValiditySeconds(60 * 60)
                .refreshTokenValiditySeconds(6 * 60 * 60)
                .and()
                .withClient(oAuthClient.getAdmin().get("id"))
                .secret(passwordEncoder.encode(oAuthClient.getAdmin().get("secret")))
                .authorizedGrantTypes(PASSWORD_GRANT, REFRESH_TOKEN_GRANT, AUTHORIZATION_CODE_GRANT)
                .scopes(READ_SCOPE, WRITE_SCOPE)
                .autoApprove(true)
                .authorities(ADMIN_AUTHORITY)
                .accessTokenValiditySeconds(60 * 30)
                .refreshTokenValiditySeconds(60 * 60);
    }

Here's the complete stacktrace:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) on project safedrive-auth-server: An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.NullPointerException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.6.RELEASE:run (default-cli) on project safedrive-auth-server: An exception occurred while running. null
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: An exception occurred while running. null
        at org.springframework.boot.maven.AbstractRunMojo$IsolatedThreadGroup.rethrowUncaughtException(AbstractRunMojo.java:511)
        at org.springframework.boot.maven.RunMojo.runWithMavenJvm(RunMojo.java:93)
        at org.springframework.boot.maven.AbstractRunMojo.run(AbstractRunMojo.java:243)
        at org.springframework.boot.maven.AbstractRunMojo.execute(AbstractRunMojo.java:198)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
        ... 19 more
Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:542)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.NullPointerException
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:380)
        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 com.adrianpop.AuthServer.main(AuthServer.java:11)
        ... 6 more
Caused by: java.lang.NullPointerException
        at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder.encode(BCryptPasswordEncoder.java:80)
        at com.adrianpop.config.AuthorizationServerConfiguration.configure(AuthorizationServerConfiguration.java:76)
        at org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration.configure(AuthorizationServerSecurityConfiguration.java:58)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:708)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
        ... 23 more
[ERROR] 
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

I've been struggling to get this to work for the few hours and it's hitting my nerves. I also checked the official documentation but the code looks fine to me. I know I am missing something simple, but I can't figure out what exactly.

Any suggestions? Thanks.

Upvotes: 0

Views: 2306

Answers (2)

Adrian Pop
Adrian Pop

Reputation: 225

So, with the help of @Mykhailo Moskura the issue was fixed. The fix is a bit weird but well, this is how Spring works here.

I've created the OAuthClientsConfiguration class as Mykhailo suggested, and the POJO mapping the properties is an inner static class. Here's how the AuthorizationServerConfiguration looks like now:

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(OAuthClientsConfiguration.class)
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    private PasswordEncoder passwordEncoder;
    private OAuthClientsConfiguration.OAuthClient oAuthClient;

    @Autowired
    public AuthorizationServerConfiguration(PasswordEncoder passwordEncoder,
                                            OAuthClientsConfiguration oAuthClientConfig) {
        this.passwordEncoder = passwordEncoder;
        this.oAuthClient = oAuthClientConfig.getOAuthClient();
    }

    ...

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                    .withClient(oAuthClient.getWebapp().get("id"))
                    .secret(passwordEncoder.encode(oAuthClient.getWebapp().get("secret")))
                    .authorizedGrantTypes(PASSWORD_GRANT, REFRESH_TOKEN_GRANT, AUTHORIZATION_CODE_GRANT)
                    .scopes(READ_SCOPE, WRITE_SCOPE)
                    .autoApprove(true)
                    .accessTokenValiditySeconds(60 * 60)
                    .refreshTokenValiditySeconds(6 * 60 * 60)
                .and()
                    .withClient(oAuthClient.getAdmin().get("id"))
                    .secret(passwordEncoder.encode(oAuthClient.getAdmin().get("secret")))
                    .authorizedGrantTypes(PASSWORD_GRANT, REFRESH_TOKEN_GRANT, AUTHORIZATION_CODE_GRANT)
                    .scopes(READ_SCOPE, WRITE_SCOPE)
                    .autoApprove(true)
                    .authorities(ADMIN_AUTHORITY) //check how to use this
                    .accessTokenValiditySeconds(60 * 30)
                    .refreshTokenValiditySeconds(60 * 60);
    }

I've also managed to store the properties in a different file that the default application.properties, by adding a new property source:

@SpringBootApplication
@PropertySources(value = {
        @PropertySource("classpath:oauth_clients.properties"),
        @PropertySource("classpath:application.properties")
})
public class AuthServer {

    public static void main(String[] args) {
        SpringApplication.run(AuthServer.class, args);
    }
}

Upvotes: 1

Mykhailo Moskura
Mykhailo Moskura

Reputation: 2211

Please try following snippet

@ConfigurationProperties(prefix = "oauth")
public class OAuthClientConfig {

    private OAuthClient OAuthClient;

    public static class OAuthClient {

        private Map<String, String> webapp = new LinkedHashMap<>();
        private Map<String, String> admin = new LinkedHashMap<>();

        public Map<String, String> getWebapp() {
            return webapp;
        }

        public void setWebapp(Map<String, String> webapp) {
            this.webapp = new LinkedHashMap<>(webapp);
        }

        public Map<String, String> getAdmin() {
            return admin;
        }

        public void setAdmin(Map<String, String> admin) {
            this.admin = new LinkedHashMap<>(admin);
        }
    }

    public OAuthClientConfig.OAuthClient getOAuthClient() {
        return OAuthClient;
    }

    public void setOAuthClient(OAuthClientConfig.OAuthClient OAuthClient) {
        this.OAuthClient = OAuthClient;
    }
}

Upvotes: 1

Related Questions