mydeveloperplanet
mydeveloperplanet

Reputation: 133

Functional Bean Registration returns no bean of type FunctionCatalog on AWS Lambda

I have tried to use the functional bean registration as mentioned in https://spring.io/blog/2018/10/22/functional-bean-registrations-in-spring-cloud-function and to deploy it to AWS Lambda. The traditional way works just fine, see code in https://github.com/mydeveloperplanet/MySpringCloudFunctionPlanet/tree/feature/aws-funtion-bean-definition

However, when I convert the application to the function bean style, the following error occurs:

2020-10-24 11:11:42.910  INFO 8 --- [           main] lambdainternal.AWSLambda                 : Starting AWSLambda on 169.254.55.181 with PID 8 (/var/runtime/lib/aws-lambda-java-runtime-0.2.0.jar started by sbx_user1051 in /var/task)
2020-10-24 11:11:42.932  INFO 8 --- [           main] lambdainternal.AWSLambda                 : No active profile set, falling back to default profiles: default
2020-10-24 11:11:44.813  INFO 8 --- [           main] lambdainternal.AWSLambda                 : Started AWSLambda in 4.559 seconds (JVM running for 6.581)
No qualifying bean of type 'org.springframework.cloud.function.context.FunctionCatalog' available: org.springframework.beans.factory.NoSuchBeanDefinitionException
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.function.context.FunctionCatalog' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1127)
    at org.springframework.cloud.function.adapter.aws.FunctionInvoker.start(FunctionInvoker.java:124)
    at org.springframework.cloud.function.adapter.aws.FunctionInvoker.<init>(FunctionInvoker.java:77)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.base/java.lang.reflect.Constructor.newInstance(Unknown Source)

END RequestId: 1e5c5105-1be5-4388-81f7-f60c77377036
REPORT RequestId: 1e5c5105-1be5-4388-81f7-f60c77377036  Duration: 6842.67 ms    Billed Duration: 6900 ms    Memory Size: 512 MB Max Memory Used: 47 MB  
Unknown application error occurred
org.springframework.beans.factory.NoSuchBeanDefinitionException

The code is the following (and available at GitHub https://github.com/mydeveloperplanet/MySpringCloudFunctionPlanet/tree/master):

@SpringBootConfiguration
public class MySpringCloudFunctionPlanetApplication implements ApplicationContextInitializer<GenericApplicationContext> {

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

    public Function<String, Boolean> containsCloud() {
        return value -> value.contains("cloud");
    }

    @Override
    public void initialize(GenericApplicationContext context) {
        context.registerBean("containsCloud", FunctionRegistration.class,
                () -> new FunctionRegistration<>(containsCloud())
                        .type(FunctionType.from(String.class).to(Boolean.class)));
    }

}

What am I missing here?

Upvotes: 4

Views: 4161

Answers (2)

David Tam
David Tam

Reputation: 546

I got this issue when I migrate springboot 2 to 3 and this is the solution in gradle. I converted it to mvn / pom.xml and it works for me:

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>3.0.11</version>
      </dependency>
    </dependencies>
    <executions>
      <execution>
        <id>shade-jar-with-dependencies</id>
        <phase>package</phase>
        <goals>
            <goal>shade</goal>
        </goals>

        <configuration>
            <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                    <resource>META-INF/spring.handlers</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                    <resource>META-INF/spring.schemas</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                    <resource>META-INF/spring.tooling</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                    <resource>INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                    <resource>META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports</resource>
                </transformer>
                <transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
                    <resource>META-INF/spring.factories</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <mainClass>${start-class}</mainClass>
                </transformer>
            </transformers>
        </configuration>
    </execution>
</executions>

Upvotes: 0

Dan Diephouse
Dan Diephouse

Reputation: 148

Well, after going down this rabbit hole I finally figured out that, for me, this was because Spring wasn't finding beans like the ContextFunctionCatalogAutoConfiguration. The reason for this in my case is that I was not inheriting from the spring-boot-starter-parent POM and there is critical configuration in there for the Maven Shade plugin to work. The Shade plugin needs to be configured to merge the spring.handlers and spring.schemas files so that Spring can discover these beans which auto register the FunctionCatalog, Jackson ObjectMapper, etc.

So to fix, you can use a configuration like this:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.4</version>
    <configuration>
        <createDependencyReducedPom>false</createDependencyReducedPom>
        <shadedArtifactAttached>true</shadedArtifactAttached>
        <shadedClassifierName>aws</shadedClassifierName>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>2.4.1</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>

            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
                        <resource>META-INF/spring.factories</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>${start-class}</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Upvotes: 2

Related Questions