GGH-431 Fixed discovery of a start class

- Ensured that FunctionClassUtils performs additional check to ensure that located star class is SpringBootApplication
- Added additional lookup to look for Main-Class if nothing was found in Start-Class primarily to support Azure
- Updated Azure samples
- Updated documentation

Resolves #431
This commit is contained in:
Oleg Zhurakousky
2019-12-12 15:03:48 +01:00
parent 12f4260271
commit 0f38ea47b8
6 changed files with 30 additions and 32 deletions

View File

@@ -15,12 +15,10 @@ Example:
```java ```java
public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> { public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> {
@FunctionName("uppercase") @FunctionName("uppercase")
public Bar execute( public Bar execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET,
@HttpTrigger(name = "req", methods = { HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<Foo>> request,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) ExecutionContext context) {
Foo foo, return handleRequest(request.getBody().get(), context);
ExecutionContext context) {
return handleRequest(foo, context);
} }
} }
``` ```
@@ -125,7 +123,7 @@ You can run the sample locally, just like the other Spring Cloud Function sample
./mvnw spring-boot:run ./mvnw spring-boot:run
--- ---
and `curl -H "Content-Type: text/plain" localhost:8080/function -d '{"value": "hello foobar"}'`. and `curl -H "Content-Type: text/plain" localhost:8080/api/uppercase -d '{"value": "hello foobar"}'`.
You will need the `az` CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime: You will need the `az` CLI app (see https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven for more detail). To deploy the function on Azure runtime:

View File

@@ -17,7 +17,6 @@
package org.springframework.cloud.function.utils; package org.springframework.cloud.function.utils;
import java.io.InputStream; import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -27,8 +26,10 @@ import java.util.jar.Manifest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/** /**
* General utility class which aggregates various class-level utility functions * General utility class which aggregates various class-level utility functions
@@ -93,15 +94,20 @@ public final class FunctionClassUtils {
try { try {
logger.info("Searching manifest: " + url); logger.info("Searching manifest: " + url);
Manifest manifest;
InputStream inputStream = null; InputStream inputStream = null;
try { try {
if ("jar".equals(url.getProtocol())) { Manifest manifest = new Manifest(url.openStream());
JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); String startClassName = manifest.getMainAttributes().getValue("Start-Class");
manifest = jarConnection.getManifest(); if (!StringUtils.hasText(startClassName)) {
startClassName = manifest.getMainAttributes().getValue("Main-Class");
} }
else {
manifest = new Manifest(url.openStream()); if (StringUtils.hasText(startClassName)) {
Class<?> startClass = ClassUtils.forName(startClassName, FunctionClassUtils.class.getClassLoader());
if (startClass.getDeclaredAnnotation(SpringBootApplication.class) != null) {
logger.info("Loaded Start Class: " + startClass);
return startClass;
}
} }
} }
finally { finally {
@@ -109,12 +115,6 @@ public final class FunctionClassUtils {
inputStream.close(); inputStream.close();
} }
} }
String startClass = manifest.getMainAttributes().getValue("Start-Class");
if (startClass != null) {
return ClassUtils.forName(startClass, FunctionClassUtils.class.getClassLoader());
}
} }
catch (Exception ex) { catch (Exception ex) {
logger.debug("Failed to determine Start-Class in manifest file of " + url, ex); logger.debug("Failed to determine Start-Class in manifest file of " + url, ex);

View File

@@ -25,10 +25,9 @@
<functionAppName>function-sample-azure</functionAppName> <functionAppName>function-sample-azure</functionAppName>
<functionAppRegion>westus</functionAppRegion> <functionAppRegion>westus</functionAppRegion>
<functionResourceGroup>java-function-group</functionResourceGroup> <functionResourceGroup>java-function-group</functionResourceGroup>
<stagingDirectory>${project.build.directory}/azure-functions/${functionAppName} <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
</stagingDirectory>
<start-class>example.Config</start-class> <start-class>example.Config</start-class>
<wrapper.version>1.0.15.RELEASE</wrapper.version> <wrapper.version>1.0.23.RELEASE</wrapper.version>
</properties> </properties>
<dependencies> <dependencies>
@@ -55,7 +54,7 @@
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-dependencies</artifactId> <artifactId>spring-cloud-function-dependencies</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version> <version>3.0.1.BUILD-SNAPSHOT</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@@ -92,7 +91,7 @@
<plugin> <plugin>
<groupId>com.microsoft.azure</groupId> <groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId> <artifactId>azure-functions-maven-plugin</artifactId>
<version>1.0.0-beta-7</version> <version>1.3.4</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>javax.xml.bind</groupId> <groupId>javax.xml.bind</groupId>

View File

@@ -1,3 +1,4 @@
{ {
"functionTimeout": "00:10:00" "functionTimeout": "00:05:00",
"version": "2.0"
} }

View File

@@ -22,7 +22,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
// @checkstyle:off
@SpringBootApplication @SpringBootApplication
public class Config { public class Config {
@@ -36,7 +35,6 @@ public class Config {
} }
} }
// @checkstyle:on
class Foo { class Foo {

View File

@@ -18,10 +18,13 @@ package example;
import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod; import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.annotation.AuthorizationLevel; import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName; import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger; import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.util.Optional;
import org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandler; import org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandler;
/** /**
@@ -30,11 +33,10 @@ import org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHa
public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> { public class FooHandler extends AzureSpringBootRequestHandler<Foo, Bar> {
@FunctionName("uppercase") @FunctionName("uppercase")
public Bar execute( public Bar execute(@HttpTrigger(name = "req", methods = {HttpMethod.GET,
@HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<Foo>> request,
HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) Foo foo,
ExecutionContext context) { ExecutionContext context) {
return handleRequest(foo, context); return handleRequest(request.getBody().get(), context);
} }
} }