Search code examples
javaspringspring-bootmavenspring-autoconfiguration

Not able to use a config created in a java library


I am creating java jar library (maven) which will contain common configuration & util classes used in many services. My project is not multi-modules, so creating a new maven project. The configuration class I am not able to use in my project. Found out there are 2 approach for this from below mentioned references.

Reference1

Reference2

  1. Adding entry in resoruces/META-INF/spring.factories in the library project (auto-configure). It is giving error of class not found. Also added package of library in scanPackage attribute of Spring Boot Application. Src code is there after all the text.

    Error: The project which uses the library is not able to find class of ouath of spring related which is a dependency in the library class

For the full error stacktrace, please see the following collapsed snippet:

12:41:07.826 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.satishch.microspace.nri.security.ReferenceSecurityConfig]; nested exception is java.lang.IllegalArgumentException: Unresolvable class definition for class [com.satishch.microspace.nri.security.ReferenceSecurityConfig]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:610) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:311) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:600) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.access$800(ConfigurationClassParser.java:111) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.lambda$processGroupImports$1(ConfigurationClassParser.java:812) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at java.util.ArrayList.forEach(ArrayList.java:1257) ~[?:1.8.0_191]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:809) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:780) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:193) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:319) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:236) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:280) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:96) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at com.satish.NriAssetMgmtServiceApplication.main(NriAssetMgmtServiceApplication.java:28) [classes/:?]
Caused by: java.lang.IllegalArgumentException: Unresolvable class definition for class [com.satishch.microspace.nri.security.ReferenceSecurityConfig]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:331) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecuritySelector.selectImports(GlobalMethodSecuritySelector.java:51) ~[spring-security-config-5.3.6.RELEASE.jar:5.3.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:581) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
... 21 more
Caused by: java.lang.NoClassDefFoundError: org/springframework/security/oauth2/config/annotation/web/configuration/ResourceServerConfigurerAdapter
at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_191]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:1.8.0_191]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[?:1.8.0_191]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) ~[?:1.8.0_191]
at java.net.URLClassLoader.access$100(URLClassLoader.java:74) ~[?:1.8.0_191]
at java.net.URLClassLoader$1.run(URLClassLoader.java:369) ~[?:1.8.0_191]
at java.net.URLClassLoader$1.run(URLClassLoader.java:363) ~[?:1.8.0_191]
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_191]
at java.net.URLClassLoader.findClass(URLClassLoader.java:362) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_191]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_191]
at java.lang.Class.forName0(Native Method) ~[?:1.8.0_191]
at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_191]
at org.springframework.util.ClassUtils.forName(ClassUtils.java:284) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:324) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecuritySelector.selectImports(GlobalMethodSecuritySelector.java:51) ~[spring-security-config-5.3.6.RELEASE.jar:5.3.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:581) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
... 21 more
Caused by: java.lang.ClassNotFoundException: org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_191]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_191]
at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_191]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:1.8.0_191]
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) ~[?:1.8.0_191]
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) ~[?:1.8.0_191]
at java.net.URLClassLoader.access$100(URLClassLoader.java:74) ~[?:1.8.0_191]
at java.net.URLClassLoader$1.run(URLClassLoader.java:369) ~[?:1.8.0_191]
at java.net.URLClassLoader$1.run(URLClassLoader.java:363) ~[?:1.8.0_191]
at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_191]
at java.net.URLClassLoader.findClass(URLClassLoader.java:362) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_191]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[?:1.8.0_191]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_191]
at java.lang.Class.forName0(Native Method) ~[?:1.8.0_191]
at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_191]
at org.springframework.util.ClassUtils.forName(ClassUtils.java:284) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.util.ClassUtils.resolveClassName(ClassUtils.java:324) ~[spring-core-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.security.config.annotation.method.configuration.GlobalMethodSecuritySelector.selectImports(GlobalMethodSecuritySelector.java:51) ~[spring-security-config-5.3.6.RELEASE.jar:5.3.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:581) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
... 21 more

 

  1. Creating annotation interface inside the library importing the config class. And, adding the annotation in any project in main class.

For the full error stacktrace, please see the following collapsed snippet:

12:34:17.903 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at com.satish.AssetMgmtServiceApplication.main(AssetMgmtServiceApplication.java:31) [classes/:?]
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:205) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:177) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:158) ~[spring-boot-2.3.8.RELEASE.jar:2.3.8.RELEASE]
... 7 more

 

Source code:

resoruces/META-INF/spring.factories (1st Approach)

 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 com.satish.security.ReferenceSecurityConfig

Config inside the library

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableResourceServer
@EnableWebSecurity
@Configuration
@ConfigurationProperties(prefix = "security")
@Data
@EqualsAndHashCode(callSuper=false)
public class ReferenceSecurityConfig extends ResourceServerConfigurerAdapter {

    private boolean bypass;
    
    @Override
    public void configure(HttpSecurity http) throws Exception {

        http
        .cors()
        .and()
        .csrf().disable();

        if (bypass) {
            http.authorizeRequests().antMatchers("/**").permitAll();
        } else {
            http.authorizeRequests()
            .antMatchers("/swagger-ui/**").permitAll()
            .antMatchers("/management").permitAll()
            .antMatchers("/management/**").permitAll()
            .antMatchers("/swagger-ui.html").permitAll()
            .antMatchers("/swagger-resources/**").permitAll()
            .antMatchers("/v2/api-docs/**").permitAll()
            .antMatchers("/webjars/**").permi tAll()
            .antMatchers("/v3/api-docs/**").permitAll()
            .antMatchers("/swagger-ui/**").permitAll()
            .antMatchers("/**").authenticated();
        }
    }
}

Annotation to use in the main project (2nd approach)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ReferenceSecurityConfig.class)
public @interface EnableCustomSecurityConfig {
}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.satish</groupId>
    <artifactId>core-lib</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>core-lib</name>
    <description>Storing common implementation</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.8.RELEASE</version>
        <relativePath />
    </parent>


    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <log4j2.version>2.17.1</log4j2.version>
        <log4j2Version>2.7</log4j2Version>
        <slf4jVersion>1.7.21</slf4jVersion>
    </properties>

    <repositories>
        <repository>
            <id>confluent</id>
            <url>https://packages.confluent.io/maven/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.1.RELEASE</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- security dependencies -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
          <groupId>org.springframework.security.oauth</groupId>
          <artifactId>spring-security-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-core</artifactId>
            <version>5.1.7.RELEASE</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${springSecurityVersion}</version>
        </dependency>
        <!-- spring-boot-starter-security -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Not sure why Lombok is not working -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Provided Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Implementation-Vendor>Satish's Companies Inc.</Implementation-Vendor>
                            <Implementation-Title>TITLE</Implementation-Title>
                            <Implementation-Version>2.2</Implementation-Version>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
            <!--plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> 
                <configuration> <fork>true</fork> <mainClass>${start-class}</mainClass> </configuration> 
                <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> 
                </executions> </plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Solution

  • First, I believe I already did a lib achieving what you're trying to do. A lot is configurable from properties (CORS, authorities mapping and more) and it is published on maven-central.

    Spring-boot starters have changed since 2.7.

    Auto-configured components should now be:

    • decorated with @AutoConfiguration
    • declared in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

    Minimal sample here with this resource

    com.c4_soft.springaddons.starter.webclient.SpringBootAutoConfiguration
    

    and that loaded file

    package com.c4_soft.springaddons.starter.webclient;
    
    import org.springframework.boot.autoconfigure.AutoConfiguration;
    import org.springframework.context.annotation.Import;
    
    @AutoConfiguration
    @Import({ C4ProxySettings.class, C4WebClientBuilderFactoryService.class })
    public class SpringBootAutoConfiguration {
    }