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 .
|
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
|
=== Supported Packaging Scenarios
|
||||||
|
|
||||||
Currently Spring Cloud Function supports several packaging scenarios to give you the most flexibility when it comes to deploying functions.
|
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>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-function-context</artifactId>
|
<artifactId>spring-cloud-function-context</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-deployer-resource-maven</artifactId>
|
||||||
|
<version>2.4.0-M1</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<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.Archive;
|
||||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
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.FunctionProperties;
|
||||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||||
import org.springframework.context.SmartLifecycle;
|
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.MapPropertySource;
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,13 +68,23 @@ public class FunctionDeployerConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SmartLifecycle functionArchiveDeployer(FunctionDeployerProperties functionProperties,
|
SmartLifecycle functionArchiveDeployer(FunctionDeployerProperties functionProperties,
|
||||||
FunctionRegistry functionRegistry, ApplicationArguments arguments) {
|
FunctionRegistry functionRegistry, ApplicationArguments arguments, @Nullable MavenProperties mavenProperties) {
|
||||||
|
|
||||||
ApplicationArguments updatedArguments = this.updateArguments(arguments);
|
ApplicationArguments updatedArguments = this.updateArguments(arguments);
|
||||||
|
|
||||||
Archive archive = null;
|
Archive archive = null;
|
||||||
try {
|
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()) {
|
if (!file.exists()) {
|
||||||
throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation() + " does not exist");
|
throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation() + " does not exist");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.function.deployer;
|
package org.springframework.cloud.function.deployer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -29,11 +31,14 @@ import reactor.util.function.Tuples;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.messaging.Message;
|
import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.support.MessageBuilder;
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,6 +53,19 @@ public class FunctionDeployerTests {
|
|||||||
System.clearProperty("spring.cloud.function.definition");
|
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>`
|
* Target function `class UpperCaseFunction implements Function<String, String>`
|
||||||
* Main/Start class present, no Spring configuration
|
* Main/Start class present, no Spring configuration
|
||||||
@@ -376,5 +394,11 @@ public class FunctionDeployerTests {
|
|||||||
|
|
||||||
@SpringBootApplication(proxyBeanMethods = false)
|
@SpringBootApplication(proxyBeanMethods = false)
|
||||||
private static class DeployerApplication {
|
private static class DeployerApplication {
|
||||||
|
@Bean
|
||||||
|
public MavenProperties mavenProperties() {
|
||||||
|
MavenProperties properties = new MavenProperties();
|
||||||
|
properties.setLocalRepository("mavenrepo/");
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user