GH-555 Add missing maven resource support to function deployer
Resolves #555
This commit is contained in:
@@ -353,7 +353,12 @@ The standard entry point is to add `spring-cloud-function-deployer` to the class
|
||||
```
|
||||
|
||||
|
||||
At a minimum the user has to provide a `spring.cloud.function.location` which is a URL or resource location for the archive containing the functions. It can optionally use a `maven:` prefix to locate the artifact via a dependency lookup (see `FunctionProperties` for complete details). A Spring Boot application is bootstrapped from the jar file, using the `MANIFEST.MF` to locate a start class, so that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function registered in the main application's `FunctionCatalog`. The registered function can be applied by code in the main application, even though it was created in an isolated class loader (by deault).
|
||||
At a minimum the user has to provide a `spring.cloud.function.location` which is a URL or resource location for the archive containing
|
||||
the functions. It can optionally use a `maven:` prefix to locate the artifact via a dependency lookup (see `FunctionProperties`
|
||||
for complete details). A Spring Boot application is bootstrapped from the jar file, using the `MANIFEST.MF` to locate a start class, so
|
||||
that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function
|
||||
registered in the main application's `FunctionCatalog`. The registered function can be applied by code in the main application, even though
|
||||
it was created in an isolated class loader (by deault).
|
||||
|
||||
Here is the example of deploying a JAR which contains an 'uppercase' function and invoking it .
|
||||
|
||||
@@ -373,6 +378,39 @@ public class DeployFunctionDemo {
|
||||
}
|
||||
```
|
||||
|
||||
And here is the example using Maven URI (taken from one of the tests in `FunctionDeployerTests`):
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
public class DeployFunctionDemo {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=maven://oz.demo:demo-uppercase:0.0.1-SNAPSHOT",
|
||||
"--spring.cloud.function.function-class=oz.demo.uppercase.MyFunction" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("myFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Keep in mind that Maven resource such as local and remote repositories, user, password and more are resolved using default MavenProperties which
|
||||
effectively use local defaults and will work for majority of cases. However if you need to customize you can simply provide a bean of type
|
||||
`MavenProperties` where you can set additional properties (see example below).
|
||||
|
||||
```java
|
||||
@Bean
|
||||
public MavenProperties mavenProperties() {
|
||||
MavenProperties properties = new MavenProperties();
|
||||
properties.setLocalRepository("target/it/");
|
||||
return properties;
|
||||
}
|
||||
```
|
||||
|
||||
=== Supported Packaging Scenarios
|
||||
|
||||
Currently Spring Cloud Function supports several packaging scenarios to give you the most flexibility when it comes to deploying functions.
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.0.RELEASE</version>
|
||||
<!-- <version>2.2.6.RELEASE</version> -->
|
||||
<relativePath />
|
||||
</parent>
|
||||
<groupId>oz.demo</groupId>
|
||||
<artifactId>demo-stream</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>demo-stream</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.boot</groupId> -->
|
||||
<!-- <artifactId>spring-boot-starter-web</artifactId> -->
|
||||
<!-- </dependency> -->
|
||||
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-starter-function-web</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-function-rsocket</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-function-web</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-function-web</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.json</groupId> -->
|
||||
<!-- <artifactId>json</artifactId> -->
|
||||
<!-- <version>20190722</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-function-web</artifactId> -->
|
||||
<!-- </dependency> -->
|
||||
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-stream</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.cloud</groupId> -->
|
||||
<!-- <artifactId>spring-cloud-stream-binder-rabbit</artifactId> -->
|
||||
<!-- <version>3.1.0-SNAPSHOT</version> -->
|
||||
<!-- </dependency> -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<!-- <build> -->
|
||||
<!-- <plugins> -->
|
||||
<!-- <plugin> -->
|
||||
<!-- <groupId>org.springframework.boot</groupId> -->
|
||||
<!-- <artifactId>spring-boot-maven-plugin</artifactId> -->
|
||||
<!-- </plugin> -->
|
||||
<!-- </plugins> -->
|
||||
<!-- </build> -->
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata modelVersion="1.1.0">
|
||||
<groupId>oz.demo</groupId>
|
||||
<artifactId>demo-stream</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<snapshot>
|
||||
<localCopy>true</localCopy>
|
||||
</snapshot>
|
||||
<lastUpdated>20200721131233</lastUpdated>
|
||||
<snapshotVersions>
|
||||
<snapshotVersion>
|
||||
<extension>jar</extension>
|
||||
<value>0.0.1-SNAPSHOT</value>
|
||||
<updated>20200721131233</updated>
|
||||
</snapshotVersion>
|
||||
<snapshotVersion>
|
||||
<extension>pom</extension>
|
||||
<value>0.0.1-SNAPSHOT</value>
|
||||
<updated>20200721131233</updated>
|
||||
</snapshotVersion>
|
||||
</snapshotVersions>
|
||||
</versioning>
|
||||
</metadata>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>oz.demo</groupId>
|
||||
<artifactId>demo-stream</artifactId>
|
||||
<versioning>
|
||||
<versions>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</versions>
|
||||
<lastUpdated>20200721131233</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
@@ -30,6 +30,11 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-deployer-resource-maven</artifactId>
|
||||
<version>2.4.0-M1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenResourceLoader;
|
||||
import org.springframework.cloud.function.context.FunctionProperties;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
@@ -42,6 +44,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -64,13 +68,23 @@ public class FunctionDeployerConfiguration {
|
||||
|
||||
@Bean
|
||||
SmartLifecycle functionArchiveDeployer(FunctionDeployerProperties functionProperties,
|
||||
FunctionRegistry functionRegistry, ApplicationArguments arguments) {
|
||||
FunctionRegistry functionRegistry, ApplicationArguments arguments, @Nullable MavenProperties mavenProperties) {
|
||||
|
||||
ApplicationArguments updatedArguments = this.updateArguments(arguments);
|
||||
|
||||
Archive archive = null;
|
||||
try {
|
||||
File file = new File(functionProperties.getLocation());
|
||||
File file;
|
||||
String location = functionProperties.getLocation();
|
||||
Assert.hasText(location, "`spring.cloud.function.location` property must be defined.");
|
||||
if (location.startsWith("maven://")) {
|
||||
MavenResourceLoader resourceLoader = new MavenResourceLoader(mavenProperties);
|
||||
file = resourceLoader.getResource(location).getFile();
|
||||
}
|
||||
else {
|
||||
file = new File(location);
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation() + " does not exist");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.function.deployer;
|
||||
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -29,11 +31,14 @@ import reactor.util.function.Tuples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
@@ -48,6 +53,19 @@ public class FunctionDeployerTests {
|
||||
System.clearProperty("spring.cloud.function.definition");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithMavenConfiguration() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=maven://oz.demo:demo-stream:0.0.1-SNAPSHOT",
|
||||
"--spring.cloud.function.function-class=oz.demo.demostream.MyFunction" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("myFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function `class UpperCaseFunction implements Function<String, String>`
|
||||
* Main/Start class present, no Spring configuration
|
||||
@@ -376,5 +394,11 @@ public class FunctionDeployerTests {
|
||||
|
||||
@SpringBootApplication(proxyBeanMethods = false)
|
||||
private static class DeployerApplication {
|
||||
@Bean
|
||||
public MavenProperties mavenProperties() {
|
||||
MavenProperties properties = new MavenProperties();
|
||||
properties.setLocalRepository("mavenrepo/");
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user