diff --git a/docs/src/main/asciidoc/adapters/aws-intro.adoc b/docs/src/main/asciidoc/adapters/aws-intro.adoc index 515519b7f..8d60da627 100644 --- a/docs/src/main/asciidoc/adapters/aws-intro.adoc +++ b/docs/src/main/asciidoc/adapters/aws-intro.adoc @@ -4,7 +4,15 @@ If your app has more than one `@Bean` of type `Function` etc. then you can choos == Notes on JAR Layout -You don't need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might need to exclude those before you create the JAR you send to AWS. A Lambda application has to be shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 separate jars (as per the sample). The sample app creates 2 jar files, one with an `aws` classifier for deploying in Lambda, and one executable (thin) jar that includes `spring-cloud-function-web` at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file manifest, using the `Start-Class` attribute (which will be added for you by the Spring Boot tooling if you use the starter parent). If there is no `Start-Class` in your manifest you can use an environment variable `MAIN_CLASS` when you deploy the function to AWS. +You don't need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might +need to exclude those before you create the JAR you send to AWS. A Lambda application has to be +shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 +separate jars (as per the sample). The sample app creates 2 jar files, one with an `aws` +classifier for deploying in Lambda, and one executable (thin) jar that includes `spring-cloud-function-web` +at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file +manifest, using the `Start-Class` attribute (which will be added for you by the Spring Boot +tooling if you use the starter parent). If there is no `Start-Class` in your manifest you can +use an environment variable or system property `MAIN_CLASS` when you deploy the function to AWS. == Upload diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java index bc7df7c9c..1067569df 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/AbstractSpringFunctionAdapterInitializer.java @@ -45,6 +45,7 @@ import org.springframework.cloud.function.context.config.FunctionContextUtils; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -89,6 +90,7 @@ public abstract class AbstractSpringFunctionAdapterInitializer implements Clo } public AbstractSpringFunctionAdapterInitializer(Class configurationClass) { + Assert.notNull(configurationClass, "'configurationClass' must not be null"); this.configurationClass = configurationClass; } @@ -254,23 +256,32 @@ public abstract class AbstractSpringFunctionAdapterInitializer implements Clo private static Class getStartClass() { ClassLoader classLoader = AbstractSpringFunctionAdapterInitializer.class.getClassLoader(); + Class mainClass = null; if (System.getenv("MAIN_CLASS") != null) { - return ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); + mainClass = ClassUtils.resolveClassName(System.getenv("MAIN_CLASS"), classLoader); } - try { - Class result = getStartClass( - Collections.list(classLoader.getResources("META-INF/MANIFEST.MF"))); - if (result == null) { - result = getStartClass(Collections - .list(classLoader.getResources("meta-inf/manifest.mf"))); + if (System.getProperty("MAIN_CLASS") != null) { + mainClass = ClassUtils.resolveClassName(System.getProperty("MAIN_CLASS"), classLoader); + } + else { + try { + Class result = getStartClass( + Collections.list(classLoader.getResources("META-INF/MANIFEST.MF"))); + if (result == null) { + result = getStartClass(Collections + .list(classLoader.getResources("meta-inf/manifest.mf"))); + } + Assert.notNull(result, "Failed to locate main class"); + mainClass = result; + } + catch (Exception ex) { + throw new IllegalStateException("Failed to discover main class. An attempt was made to discover " + + "main class as 'MAIN_CLASS' environment variable, system property as well as " + + "entry in META-INF/MANIFEST.MF (in that order).", ex); } - logger.info("Main class: " + result); - return result; - } - catch (Exception ex) { - logger.error("Failed to find main class", ex); - return null; } + logger.info("Main class: " + mainClass); + return mainClass; } private static Class getStartClass(List list) { diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java index f6dc36217..125d2d86f 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/SpringFunctionAdapterInitializerTests.java @@ -54,6 +54,26 @@ public class SpringFunctionAdapterInitializerTests { } } + @Test(expected = IllegalArgumentException.class) + public void nullSource() { + this.initializer = new AbstractSpringFunctionAdapterInitializer(null) { + + }; + } + + @Test + public void sourceAsMainClassProperty() { + try { + System.setProperty("MAIN_CLASS", FluxFunctionConfig.class.getName()); + this.initializer = new AbstractSpringFunctionAdapterInitializer() { + + }; + } + finally { + System.clearProperty("MAIN_CLASS"); + } + } + @Test public void functionBean() { this.initializer = new AbstractSpringFunctionAdapterInitializer(FluxFunctionConfig.class) {