diff --git a/spring-cloud-function-deployer/src/it/support/pom.xml b/spring-cloud-function-deployer/src/it/support/pom.xml
index 823acf6de..0eba99cc3 100644
--- a/spring-cloud-function-deployer/src/it/support/pom.xml
+++ b/spring-cloud-function-deployer/src/it/support/pom.xml
@@ -1,106 +1,129 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
- com.example
- function-sample
- 1.0.0.M1
- jar
+ com.example
+ function-sample
+ 1.0.0.M1
+ jar
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.0.3.RELEASE
-
-
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.3.RELEASE
+
+
-
- 1.8
- 2.0.0.BUILD-SNAPSHOT
- 1.0.12.RELEASE
-
+
+ 1.8
+ 2.0.0.BUILD-SNAPSHOT
+ 1.0.12.RELEASE
+
-
-
- org.springframework.cloud
- spring-cloud-function-context
- ${spring-cloud-function.version}
-
-
- org.springframework.boot
- spring-boot-starter
-
-
+
+
+ org.springframework.cloud
+ spring-cloud-function-context
+ ${spring-cloud-function.version}
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
- exec
-
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/libs-snapshot-local
-
- true
-
-
- false
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/libs-milestone-local
-
- false
-
-
-
- spring-releases
- Spring Releases
- https://repo.spring.io/release
-
- false
-
-
-
-
-
- spring-snapshots
- Spring Snapshots
- https://repo.spring.io/libs-snapshot-local
-
- true
-
-
- false
-
-
-
- spring-milestones
- Spring Milestones
- https://repo.spring.io/libs-milestone-local
-
- false
-
-
-
- spring-releases
- Spring Releases
- https://repo.spring.io/libs-release-local
-
- false
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ exec
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack
+ package
+
+ unpack
+
+
+
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+ exec
+
+
+
+
+
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/libs-snapshot-local
+
+ true
+
+
+ false
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/libs-milestone-local
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/release
+
+ false
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/libs-snapshot-local
+
+ true
+
+
+ false
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/libs-milestone-local
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/libs-release-local
+
+ false
+
+
+
diff --git a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java
index 3b5b867b0..5ba8e8bcb 100644
--- a/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java
+++ b/spring-cloud-function-deployer/src/main/java/org/springframework/cloud/function/deployer/FunctionCreatorConfiguration.java
@@ -295,13 +295,19 @@ class FunctionCreatorConfiguration {
if (!"file".equals(url.getProtocol())) {
return Collections.singletonList(url);
}
- if (!url.toString().endsWith(".jar")) {
- return Collections.singletonList(url);
- }
try {
File file = new File(url.toURI());
if (file.exists()) {
- JarFileArchive archive = new JarFileArchive(file);
+ Archive archive;
+ if (!url.toString().endsWith(".jar")) {
+ if (!new File(file, "BOOT-INF").exists()) {
+ return Collections.singletonList(url);
+ }
+ archive = new ExplodedArchive(file);
+ }
+ else {
+ archive = new JarFileArchive(file);
+ }
return Arrays
.asList(new ComputeLauncher(archive).getClassLoaderUrls());
}
diff --git a/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SpringFunctionAppExplodedConfigurationTests.java b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SpringFunctionAppExplodedConfigurationTests.java
new file mode 100644
index 000000000..7926ec9ea
--- /dev/null
+++ b/spring-cloud-function-deployer/src/test/java/org/springframework/cloud/function/deployer/SpringFunctionAppExplodedConfigurationTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.function.deployer;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.rule.OutputCapture;
+import org.springframework.cloud.function.context.FunctionCatalog;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsString;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = FunctionDeployerConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
+@TestPropertySource(properties = {
+ "function.location=file:target/it/support/target/dependency", })
+public abstract class SpringFunctionAppExplodedConfigurationTests {
+
+ @Autowired
+ protected FunctionCatalog catalog;
+
+ @EnableAutoConfiguration
+ @TestPropertySource(properties = { "function.bean=myEmitter",
+ "function.main=com.example.functions.FunctionApp" })
+ public static class SourceTests extends SpringFunctionAppExplodedConfigurationTests {
+
+ @Test
+ public void test() throws Exception {
+ Supplier> function = catalog.lookup(Supplier.class, "function0");
+ assertThat(function.get().blockFirst()).isEqualTo("one");
+ }
+
+ }
+
+ @EnableAutoConfiguration
+ @TestPropertySource(properties = { "function.bean=myEmitter,myCounter" })
+ public static class CompositeTests extends SpringFunctionAppExplodedConfigurationTests {
+
+ @Test
+ public void test() throws Exception {
+ Supplier> function = catalog.lookup(Supplier.class,
+ "function0|function1");
+ assertThat(function.get().blockFirst()).isEqualTo(3);
+ }
+
+ }
+
+ @EnableAutoConfiguration
+ @TestPropertySource(properties = { "function.bean=myCounter" })
+ public static class ProcessorTests extends SpringFunctionAppExplodedConfigurationTests {
+
+ @Test
+ public void test() throws Exception {
+ Function, Flux> function = catalog
+ .lookup(Function.class, "function0");
+ assertThat(function.apply(Flux.just("spam")).blockFirst()).isEqualTo(4);
+ }
+
+ }
+
+ @EnableAutoConfiguration
+ @TestPropertySource(properties = { "function.bean=myDoubler" })
+ public static class SinkTests extends SpringFunctionAppExplodedConfigurationTests {
+
+ @Rule
+ public OutputCapture capture = new OutputCapture();
+
+ @Test
+ public void test() throws Exception {
+ // Can't assert side effects.
+ Function, Mono> function = catalog.lookup(Function.class,
+ "function0");
+ function.apply(Flux.just(5)).block();
+ capture.expect(containsString(String.format("10%n")));
+ }
+
+ }
+
+}
\ No newline at end of file