Renamed deployer-new to deployer, removed old deployer
This commit is contained in:
31
spring-cloud-function-deployer/.gitignore
vendored
Normal file
31
spring-cloud-function-deployer/.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
114
spring-cloud-function-deployer/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
114
spring-cloud-function-deployer/.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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
|
||||
|
||||
https://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.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL =
|
||||
"https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: : " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
BIN
spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
1
spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
1
spring-cloud-function-deployer/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
|
||||
@@ -1,21 +0,0 @@
|
||||
Spring Cloud Function Deployer is an library for building apps that can deploy functions packaged as jars. It can deploy a basic Spring Cloud Function app from a jar with locally cached dependencies in about 500ms (compared to 1500ms for the same application launched from cold). It can be used in a pool as a "warm" JVM to deploy functions quicker than they could be started from scratch. Example usage:
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableFunctionDeployer
|
||||
public class FunctionApplication {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
new ApplicationBootstrap().run(FunctionApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
There is a main class in the jar that alread looks like this. You can use it like that or you can create your own copy if you want to customize it. The `ApplicationBootstrap` is a utility that replaces `SpringApplication`, creating a class loader hierarchy that works with the function configuration. It needs to be launched with configuration for the `FunctionProperties`:
|
||||
|
||||
| Option | Description |
|
||||
|--------|----------------------|
|
||||
| `function.location` | Mandatory archive location(s) for building the classpath of the function. |
|
||||
| `function.bean` | Mandatory bean class or name (if `function.main` is provided) to create the function. If multi-valued, the function is composed (outputs piped to inputs) |
|
||||
| `function.main` | The main `@SpringBootApplication` to launch (optional). |
|
||||
286
spring-cloud-function-deployer/mvnw
vendored
Executable file
286
spring-cloud-function-deployer/mvnw
vendored
Executable file
@@ -0,0 +1,286 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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
|
||||
#
|
||||
# https://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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
curl -o "$wrapperJarPath" "$jarUrl"
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
161
spring-cloud-function-deployer/mvnw.cmd
vendored
Normal file
161
spring-cloud-function-deployer/mvnw.cmd
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
echo Found %WRAPPER_JAR%
|
||||
) else (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
@@ -1,48 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-function-deployer</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-function-deployer</name>
|
||||
<description>Spring Cloud Function Web Support</description>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-function-deployer-new</name>
|
||||
<description>Spring Cloud Function Web Support</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-parent</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<wrapper.version>1.0.10.RELEASE</wrapper.version>
|
||||
<spring.cloud.deployer.version>2.0.2.BUILD-SNAPSHOT</spring.cloud.deployer.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-deployer-resource-maven</artifactId>
|
||||
<version>${spring.cloud.deployer.version}</version>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-deployer-resource-support</artifactId>
|
||||
<version>${spring.cloud.deployer.version}</version>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -51,29 +37,24 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot.experimental</groupId>
|
||||
<artifactId>spring-boot-thin-layout</artifactId>
|
||||
<version>${wrapper.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<!-- <plugin> -->
|
||||
<!-- <groupId>org.springframework.boot</groupId> -->
|
||||
<!-- <artifactId>spring-boot-maven-plugin</artifactId> -->
|
||||
<!-- <dependencies> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>org.springframework.boot.experimental</groupId> -->
|
||||
<!-- <artifactId>spring-boot-thin-layout</artifactId> -->
|
||||
<!-- <version>${wrapper.version}</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- </dependencies> -->
|
||||
<!-- </plugin> -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-invoker-plugin</artifactId>
|
||||
@@ -100,56 +81,37 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings
|
||||
only. It has no influence on the Maven build itself. -->
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>
|
||||
org.apache.maven.plugins
|
||||
</groupId>
|
||||
<artifactId>
|
||||
maven-invoker-plugin
|
||||
</artifactId>
|
||||
<versionRange>
|
||||
[1.10,)
|
||||
</versionRange>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore></ignore>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>invoke</id>
|
||||
<activation>
|
||||
<file>
|
||||
<missing>target/it/flux/pom.xml</missing>
|
||||
</file>
|
||||
</activation>
|
||||
<properties>
|
||||
<skip.invoke>false</skip.invoke>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
||||
|
||||
71
spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml
Normal file
71
spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootapp-multi</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,29 @@
|
||||
package function.example;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MyFn implements Function<Tuple2<Flux<String>, Flux<Integer>>, Tuple2<Flux<Double>, Flux<String>>> {
|
||||
|
||||
|
||||
@Override
|
||||
public Tuple2<Flux<Double>, Flux<String>> apply(Tuple2<Flux<String>, Flux<Integer>> inputs) {
|
||||
Flux<String> words = inputs.getT1();
|
||||
Flux<Integer> numbers = inputs.getT2().publish().autoConnect(2);
|
||||
|
||||
|
||||
Flux<Double> avg = numbers.buffer(2, 1)
|
||||
.map(l -> l.stream().mapToInt(Integer::intValue).average().getAsDouble())
|
||||
.take(3);
|
||||
|
||||
Flux<String> repeated = words.zipWith(numbers)
|
||||
.flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1())));
|
||||
|
||||
return Tuples.of(avg, repeated);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package function.example;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@SpringBootApplication
|
||||
public class RepeaterApplication {
|
||||
|
||||
@Bean
|
||||
public Function<Tuple2<Flux<String>, Flux<Integer>>,
|
||||
Tuple2<Flux<Double>, Flux<String>>
|
||||
> fn() {
|
||||
return new MyFn();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RepeaterApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootapp-with-javax</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>javax.mail</artifactId>
|
||||
<version>1.6.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,57 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SimpleFunctionAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SimpleFunctionAppApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<String, String> uppercase() {
|
||||
System.out.println("==> CREATING 'uppercase' FUNCTION bean");
|
||||
return new UpperCaseFunction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Person, Person> uppercasePerson() {
|
||||
System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean");
|
||||
return person -> {
|
||||
Person p = new Person();
|
||||
p.setId(person.getId());
|
||||
p.setName(person.getName().toUpperCase());
|
||||
return p;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Person {
|
||||
private String name;
|
||||
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.mail.Address;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
try {
|
||||
Address address = new InternetAddress(value);
|
||||
}
|
||||
catch (AddressException e) {
|
||||
throw new IllegalStateException("Failed to create and address: ", e);
|
||||
}
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootapp-with-scf</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,57 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SimpleFunctionAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SimpleFunctionAppApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<String, String> uppercase() {
|
||||
System.out.println("==> CREATING 'uppercase' FUNCTION bean");
|
||||
return new UpperCaseFunction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Person, Person> uppercasePerson() {
|
||||
System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean");
|
||||
return person -> {
|
||||
Person p = new Person();
|
||||
p.setId(person.getId());
|
||||
p.setName(person.getName().toUpperCase());
|
||||
return p;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Person {
|
||||
private String name;
|
||||
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
66
spring-cloud-function-deployer/src/it/bootapp/pom.xml
Normal file
66
spring-cloud-function-deployer/src/it/bootapp/pom.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootapp</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,57 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SimpleFunctionAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SimpleFunctionAppApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<String, String> uppercase() {
|
||||
System.out.println("==> CREATING 'uppercase' FUNCTION bean");
|
||||
return new UpperCaseFunction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Person, Person> uppercasePerson() {
|
||||
System.out.println("==> CREATING 'uppercasePerson' FUNCTION bean");
|
||||
return person -> {
|
||||
Person p = new Person();
|
||||
p.setId(person.getId());
|
||||
p.setName(person.getName().toUpperCase());
|
||||
return p;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class Person {
|
||||
private String name;
|
||||
|
||||
private int id;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
72
spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml
Normal file
72
spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootjar-multi</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<layout>NONE</layout>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,27 @@
|
||||
package function.example;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Repeater implements Function<Tuple2<Flux<String>, Flux<Integer>>, Tuple2<Flux<String>, Flux<Integer>>> {
|
||||
|
||||
@Override
|
||||
public Tuple2<Flux<String>, Flux<Integer>> apply(Tuple2<Flux<String>, Flux<Integer>> inputs) {
|
||||
Flux<String> stringFlux = inputs.getT1();
|
||||
Flux<Integer> integerFlux = inputs.getT2();
|
||||
Flux<Integer> sharedIntFlux = integerFlux.publish().autoConnect(2);
|
||||
|
||||
Flux<String> repeated = stringFlux.zipWith(sharedIntFlux)
|
||||
.flatMap(t -> Flux.fromIterable(Collections.nCopies(t.getT2(), t.getT1())));
|
||||
|
||||
Flux<Integer> sum = sharedIntFlux.buffer(2, 1)
|
||||
.map(l -> l.stream().mapToInt(Integer::intValue).sum())
|
||||
;
|
||||
|
||||
return Tuples.of(repeated, sum);
|
||||
}
|
||||
}
|
||||
66
spring-cloud-function-deployer/src/it/bootjar/pom.xml
Normal file
66
spring-cloud-function-deployer/src/it/bootjar/pom.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootjar</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
public class SimpleFunctionAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SimpleFunctionAppApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
78
spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml
Normal file
78
spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>bootjarnostart</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<layout>NONE</layout>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Function-Class>function.example.UpperCaseFunction</Function-Class>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>flux-sample</artifactId>
|
||||
<version>1.0.0.RC1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<!-- <repositories> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-snapshots</id> -->
|
||||
<!-- <name>Spring Snapshots</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>true</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- <releases> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </releases> -->
|
||||
<!-- </repository> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-milestones</id> -->
|
||||
<!-- <name>Spring Milestones</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </repository> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-releases</id> -->
|
||||
<!-- <name>Spring Releases</name> -->
|
||||
<!-- <url>https://repo.spring.io/release</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </repository> -->
|
||||
<!-- </repositories> -->
|
||||
<!-- <pluginRepositories> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-snapshots</id> -->
|
||||
<!-- <name>Spring Snapshots</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>true</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- <releases> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </releases> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-milestones</id> -->
|
||||
<!-- <name>Spring Milestones</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-releases</id> -->
|
||||
<!-- <name>Spring Releases</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-release-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- </pluginRepositories> -->
|
||||
|
||||
</project>
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 com.example.functions;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class FunctionApp {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(FunctionApp.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Function<Flux<Foo>, Flux<Foo>> foos() {
|
||||
return flux -> flux.map(value -> new Foo(value.getValue().toUpperCase()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Foo {
|
||||
|
||||
private String value;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Foo [value=" + this.value + "]";
|
||||
}
|
||||
|
||||
}
|
||||
32
spring-cloud-function-deployer/src/it/simplestjar/pom.xml
Normal file
32
spring-cloud-function-deployer/src/it/simplestjar/pom.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>function.example</groupId>
|
||||
<artifactId>simplestjar</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>simplestjar</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
package function.example;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UpperCaseFunction implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
System.out.println("Uppercasing " + value);
|
||||
return value.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>function-sample</artifactId>
|
||||
<version>1.0.0.M1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.0.M5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-cloud-function.version>3.0.0.BUILD-SNAPSHOT</spring-cloud-function.version>
|
||||
<wrapper.version>1.0.17.RELEASE</wrapper.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<version>${spring-cloud-function.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>exec</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>exec</classifier>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<!-- <repositories> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-snapshots</id> -->
|
||||
<!-- <name>Spring Snapshots</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>true</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- <releases> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </releases> -->
|
||||
<!-- </repository> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-milestones</id> -->
|
||||
<!-- <name>Spring Milestones</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </repository> -->
|
||||
<!-- <repository> -->
|
||||
<!-- <id>spring-releases</id> -->
|
||||
<!-- <name>Spring Releases</name> -->
|
||||
<!-- <url>https://repo.spring.io/release</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </repository> -->
|
||||
<!-- </repositories> -->
|
||||
<!-- <pluginRepositories> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-snapshots</id> -->
|
||||
<!-- <name>Spring Snapshots</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-snapshot-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>true</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- <releases> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </releases> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-milestones</id> -->
|
||||
<!-- <name>Spring Milestones</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-milestone-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- <pluginRepository> -->
|
||||
<!-- <id>spring-releases</id> -->
|
||||
<!-- <name>Spring Releases</name> -->
|
||||
<!-- <url>https://repo.spring.io/libs-release-local</url> -->
|
||||
<!-- <snapshots> -->
|
||||
<!-- <enabled>false</enabled> -->
|
||||
<!-- </snapshots> -->
|
||||
<!-- </pluginRepository> -->
|
||||
<!-- </pluginRepositories> -->
|
||||
|
||||
</project>
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 com.example.functions;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class DoubleLogger implements Consumer<Integer> {
|
||||
|
||||
@Override
|
||||
public void accept(Integer i) {
|
||||
System.out.println(2 * i);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 com.example.functions;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class Emitter implements Supplier<String> {
|
||||
|
||||
private int i = 0;
|
||||
|
||||
private String[] values = {"one", "two", "three", "four"};
|
||||
|
||||
@Override
|
||||
public String get() {
|
||||
return values[i++ % values.length];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 com.example.functions;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class FunctionApp {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(FunctionApp.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DoubleLogger myDoubler() {
|
||||
return new DoubleLogger();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Emitter myEmitter() {
|
||||
return new Emitter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LengthCounter myCounter() {
|
||||
return new LengthCounter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 com.example.functions;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class LengthCounter implements Function<String, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer apply(String string) {
|
||||
return string.length();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility class to launch a Spring Boot application (optionally) in an isolated class
|
||||
* loader. The class loader is created in such a way that it is mostly a copy of the
|
||||
* current class loader (i.e. the one that loaded this class), but has a parent containing
|
||||
* reactor-core (if present). It can then share the reactor dependency with other class
|
||||
* loaders that the app itself creates, without any other classes being shared, other than
|
||||
* the core JDK.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ApplicationBootstrap {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ApplicationBootstrap.class);
|
||||
|
||||
private ApplicationRunner runner;
|
||||
|
||||
private URLClassLoader classLoader;
|
||||
|
||||
private static boolean isolated(String[] args) {
|
||||
for (String arg : args) {
|
||||
if (arg.equals("--function.runner.isolated=false")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the provided main class as a Spring Boot application with the provided command
|
||||
* line arguments.
|
||||
* @param mainClass the main class
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public void run(Class<?> mainClass, String... args) {
|
||||
if (ApplicationBootstrap.isolated(args)) {
|
||||
runner(mainClass).run(args);
|
||||
}
|
||||
else {
|
||||
SpringApplication.run(mainClass, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the resources used by this instance, if any. Called automatically on a
|
||||
* runtime shutdown hook.
|
||||
*/
|
||||
public void close() {
|
||||
if (this.runner != null) {
|
||||
this.runner.close();
|
||||
this.runner = null;
|
||||
}
|
||||
if (this.classLoader != null) {
|
||||
try {
|
||||
this.classLoader.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot close ClassLoader", e);
|
||||
}
|
||||
finally {
|
||||
this.classLoader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationRunner getRunner() {
|
||||
return this.runner;
|
||||
}
|
||||
|
||||
private ApplicationRunner runner(Class<?> mainClass) {
|
||||
if (this.runner == null) {
|
||||
synchronized (this) {
|
||||
if (this.runner == null) {
|
||||
this.classLoader = createClassLoader(mainClass);
|
||||
this.runner = new ApplicationRunner(this.classLoader,
|
||||
mainClass.getName());
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.runner;
|
||||
}
|
||||
|
||||
private URLClassLoader createClassLoader(Class<?> mainClass) {
|
||||
URL[] urls = findClassPath(mainClass);
|
||||
if (urls.length == 1) {
|
||||
URL[] classpath = extractClasspath(urls[0]);
|
||||
if (classpath != null) {
|
||||
urls = classpath;
|
||||
}
|
||||
}
|
||||
List<URL> child = new ArrayList<>();
|
||||
List<URL> parent = new ArrayList<>();
|
||||
for (URL url : urls) {
|
||||
child.add(url);
|
||||
}
|
||||
for (URL url : urls) {
|
||||
if (isRoot(StringUtils.getFilename(clean(url.toString())))) {
|
||||
parent.add(url);
|
||||
child.remove(url);
|
||||
}
|
||||
}
|
||||
logger.debug("Parent: " + parent);
|
||||
logger.debug("Child: " + child);
|
||||
ClassLoader base = getClass().getClassLoader();
|
||||
if (!parent.isEmpty()) {
|
||||
base = new URLClassLoader(parent.toArray(new URL[0]), base.getParent());
|
||||
}
|
||||
return new URLClassLoader(child.toArray(new URL[0]), base);
|
||||
}
|
||||
|
||||
private URL[] findClassPath(Class<?> mainClass) {
|
||||
ClassLoader base = mainClass.getClassLoader();
|
||||
if (!(base instanceof URLClassLoader)) {
|
||||
try {
|
||||
// Guess the classpath, based on where we can resolve existing resources
|
||||
List<URL> list = Collections
|
||||
.list(mainClass.getClassLoader().getResources("META-INF"));
|
||||
List<URL> result = new ArrayList<>();
|
||||
result.add(mainClass.getProtectionDomain().getCodeSource().getLocation());
|
||||
for (URL url : list) {
|
||||
String path = url.toString();
|
||||
path = path.substring(0, path.length() - "/META-INF".length()) + "/";
|
||||
result.add(new URL(path));
|
||||
}
|
||||
return result.toArray(new URL[result.size()]);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot find class path", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@SuppressWarnings("resource")
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) base;
|
||||
return urlClassLoader.getURLs();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRoot(String file) {
|
||||
return file.startsWith("reactor-core") || file.startsWith("reactive-streams");
|
||||
}
|
||||
|
||||
private String clean(String jar) {
|
||||
// This works with fat jars like Spring Boot where the path elements look like
|
||||
// jar:file:...something.jar!/.
|
||||
return jar.endsWith("!/") ? jar.substring(0, jar.length() - 2) : jar;
|
||||
}
|
||||
|
||||
private URL[] extractClasspath(URL url) {
|
||||
// This works for a jar indirection like in surefire and IntelliJ
|
||||
if (url.toString().endsWith(".jar")) {
|
||||
JarFile jar;
|
||||
try {
|
||||
jar = new JarFile(new File(url.toURI()));
|
||||
String path = jar.getManifest().getMainAttributes()
|
||||
.getValue("Class-Path");
|
||||
if (path != null) {
|
||||
List<URL> result = new ArrayList<>();
|
||||
for (String element : path.split(" ")) {
|
||||
result.add(new URL(element));
|
||||
}
|
||||
return result.toArray(new URL[0]);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.support.LiveBeansView;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Driver class for running a Spring Boot application via an isolated classpath.
|
||||
* Initialize an instance of this class with the class loader to be used and the name of
|
||||
* the main class (usually a <code>@SpringBootApplication</code>), and then
|
||||
* {@link #run(String...)} it, cleaning up with a call to {@link #close()}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
public class ApplicationRunner {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ApplicationRunner.class);
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final String source;
|
||||
|
||||
private final SpelParserConfiguration config;
|
||||
|
||||
private final StandardTypeLocator typeLocator;
|
||||
|
||||
private StandardEvaluationContext app;
|
||||
|
||||
public ApplicationRunner(ClassLoader classLoader, String source) {
|
||||
this.classLoader = classLoader;
|
||||
this.source = source;
|
||||
this.config = new SpelParserConfiguration(null, this.classLoader);
|
||||
this.typeLocator = new StandardTypeLocator(this.classLoader);
|
||||
}
|
||||
|
||||
public void run(String... args) {
|
||||
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
ClassUtils.overrideThreadContextClassLoader(this.classLoader);
|
||||
Class<?> cls = this.classLoader.loadClass(ContextRunner.class.getName());
|
||||
this.app = new StandardEvaluationContext(
|
||||
cls.getDeclaredConstructor().newInstance());
|
||||
this.app.setTypeLocator(new StandardTypeLocator(this.classLoader));
|
||||
runContext(this.source, defaultProperties(UUID.randomUUID().toString()),
|
||||
args);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Cannot deploy", e);
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(contextLoader);
|
||||
}
|
||||
RuntimeException e = getError();
|
||||
if (e != null) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> defaultProperties(String id) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(LiveBeansView.MBEAN_DOMAIN_PROPERTY_NAME, "function-deployer-" + id);
|
||||
map.put("spring.jmx.default-domain", "function-deployer-" + id);
|
||||
map.put("spring.jmx.enabled", "false");
|
||||
return map;
|
||||
}
|
||||
|
||||
public Object getBean(String name) {
|
||||
if (this.app != null) {
|
||||
if (containsBeanByName(name)) {
|
||||
return getBeanByName(name);
|
||||
}
|
||||
try {
|
||||
return getBeanByType(name);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// not there
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean containsBeanByName(String name) {
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.containsBean(\"" + name + "\")");
|
||||
return parsed.getValue(this.app, Boolean.class);
|
||||
}
|
||||
|
||||
private Object getBeanByName(String name) {
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.getBean(\"" + name + "\")");
|
||||
return parsed.getValue(this.app);
|
||||
}
|
||||
|
||||
private Object getBeanByType(String name) {
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.getBean(T(" + name + "))");
|
||||
return parsed.getValue(this.app);
|
||||
}
|
||||
|
||||
public boolean containsBean(String name) {
|
||||
if (this.app != null) {
|
||||
if (containsBeanByName(name)) {
|
||||
return true;
|
||||
}
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.getBeansOfType(T(" + name + "))");
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> beans = (Map<String, Object>) parsed
|
||||
.getValue(this.app);
|
||||
return !beans.isEmpty();
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the bean names in the application context for a given type (by its fully
|
||||
* qualified name).
|
||||
* @param type the name of the type (Class)
|
||||
* @return the bean names of that type
|
||||
*/
|
||||
public Set<String> getBeanNames(String type) {
|
||||
if (this.app != null) {
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.getBeansOfType(T(" + type + "))");
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> beans = (Map<String, Object>) parsed
|
||||
.getValue(this.app);
|
||||
return beans.keySet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public Object evaluate(String expression, Object root, Object... attrs) {
|
||||
Expression parsed = new SpelExpressionParser(this.config)
|
||||
.parseExpression(expression);
|
||||
StandardEvaluationContext context = new StandardEvaluationContext(root);
|
||||
context.setTypeLocator(this.typeLocator);
|
||||
if (attrs.length % 2 != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Context attributes must be name, value pairs");
|
||||
}
|
||||
for (int i = 0; i < attrs.length / 2; i++) {
|
||||
String name = (String) attrs[2 * i];
|
||||
Object value = attrs[2 * i + 1];
|
||||
context.setVariable(name, value);
|
||||
}
|
||||
return parsed.getValue(context);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
if (this.app == null) {
|
||||
return false;
|
||||
}
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("context.isRunning()");
|
||||
return parsed.getValue(this.app, Boolean.class);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
closeContext();
|
||||
}
|
||||
|
||||
private RuntimeException getError() {
|
||||
if (this.app == null) {
|
||||
return null;
|
||||
}
|
||||
Expression parsed = new SpelExpressionParser().parseExpression("error");
|
||||
Throwable e = parsed.getValue(this.app, Throwable.class);
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
if (e instanceof RuntimeException) {
|
||||
return (RuntimeException) e;
|
||||
}
|
||||
return new IllegalStateException("Cannot launch", e);
|
||||
}
|
||||
|
||||
private void runContext(String mainClass, Map<String, String> properties,
|
||||
String... args) {
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("run(#main,#properties,#args)");
|
||||
StandardEvaluationContext context = this.app;
|
||||
context.setVariable("main", mainClass);
|
||||
context.setVariable("properties", properties);
|
||||
context.setVariable("args", args);
|
||||
parsed.getValue(context);
|
||||
}
|
||||
|
||||
private void closeContext() {
|
||||
if (this.app != null) {
|
||||
Expression parsed = new SpelExpressionParser().parseExpression("close()");
|
||||
parsed.getValue(this.app);
|
||||
this.app = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.function.context.FunctionalSpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.SimpleCommandLinePropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Utility class for starting a Spring Boot application in a separate thread. Best used
|
||||
* from an isolated class loader, e.g. through {@link ApplicationRunner}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ContextRunner {
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private Thread runThread;
|
||||
|
||||
private volatile boolean running = false;
|
||||
|
||||
private Throwable error;
|
||||
|
||||
private long timeout = 120000;
|
||||
|
||||
private static boolean isFunctional(StandardEnvironment environment) {
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.springframework.cloud.function.context.FunctionalSpringApplication",
|
||||
null)) {
|
||||
return false;
|
||||
}
|
||||
return environment.resolvePlaceholders("${spring.functional.enabled:true}")
|
||||
.equals("true");
|
||||
}
|
||||
|
||||
public void run(final String source, final Map<String, Object> properties,
|
||||
final String... args) {
|
||||
// Run in new thread to ensure that the context classloader is setup
|
||||
this.runThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
resetUrlHandler();
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
environment.getPropertySources().addAfter(
|
||||
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
|
||||
new MapPropertySource("appDeployer", properties));
|
||||
if (args != null && args.length > 0) {
|
||||
environment.getPropertySources().addFirst(
|
||||
new SimpleCommandLinePropertySource("args", args));
|
||||
}
|
||||
ContextRunner.this.running = true;
|
||||
Class<?> sourceClass = ClassUtils.resolveClassName(source, null);
|
||||
SpringApplication builder = builder(sourceClass, environment);
|
||||
ContextRunner.this.context = builder.run(args);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
ContextRunner.this.error = ex;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
this.runThread.start();
|
||||
try {
|
||||
this.runThread.join(this.timeout);
|
||||
this.running = this.context != null && this.context.isRunning();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
this.running = false;
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
resetUrlHandler();
|
||||
}
|
||||
// TODO: JDBC leak protection?
|
||||
this.running = false;
|
||||
this.runThread.setContextClassLoader(null);
|
||||
this.runThread = null;
|
||||
}
|
||||
|
||||
public ConfigurableApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
private void resetUrlHandler() {
|
||||
if (ClassUtils.isPresent(
|
||||
"org.apache.catalina.webresources.TomcatURLStreamHandlerFactory", null)) {
|
||||
setField(ClassUtils.resolveClassName(
|
||||
"org.apache.catalina.webresources.TomcatURLStreamHandlerFactory",
|
||||
null), "instance", null);
|
||||
setField(URL.class, "factory", null);
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(Class<?> type, String name, Object value) {
|
||||
Field field = ReflectionUtils.findField(type, name);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
ReflectionUtils.setField(field, null, value);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
|
||||
public Throwable getError() {
|
||||
return this.error;
|
||||
}
|
||||
|
||||
private SpringApplication builder(Class<?> type, StandardEnvironment environment) {
|
||||
SpringApplication application;
|
||||
if (!isFunctional(environment)) {
|
||||
application = new SpringApplication(type);
|
||||
}
|
||||
else {
|
||||
application = FunctionalSpringApplicationCreator.create(type);
|
||||
}
|
||||
application.setEnvironment(environment);
|
||||
application.setRegisterShutdownHook(false);
|
||||
return application;
|
||||
}
|
||||
|
||||
private static class FunctionalSpringApplicationCreator {
|
||||
|
||||
public static SpringApplication create(Class<?> type) {
|
||||
return new FunctionalSpringApplication(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2019-2019 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
|
||||
*
|
||||
* https://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.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 3.0
|
||||
*/
|
||||
abstract class DeployerContextUtils {
|
||||
|
||||
public static Type findType(BeanFactory beanFactory, String name) {
|
||||
ConfigurableListableBeanFactory registry = (ConfigurableListableBeanFactory) beanFactory;
|
||||
AbstractBeanDefinition definition = (AbstractBeanDefinition) registry.getBeanDefinition(name);
|
||||
|
||||
Object source = definition.getSource();
|
||||
|
||||
Type param = null;
|
||||
if (source instanceof MethodMetadata) {
|
||||
param = findBeanType(definition, ((MethodMetadata) source).getDeclaringClassName(), ((MethodMetadata) source).getMethodName());
|
||||
}
|
||||
else if (source instanceof Resource) {
|
||||
param = registry.getType(name);
|
||||
}
|
||||
else {
|
||||
ResolvableType type = (ResolvableType) getField(definition, "targetType");
|
||||
if (type != null) {
|
||||
param = type.getType();
|
||||
}
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
private static Type findBeanType(AbstractBeanDefinition definition, String declaringClassName, String methodName) {
|
||||
Class<?> factory = ClassUtils.resolveClassName(declaringClassName, null);
|
||||
Class<?>[] params = getParamTypes(factory, definition);
|
||||
Method method = ReflectionUtils.findMethod(factory, methodName,
|
||||
params);
|
||||
Type type = method.getGenericReturnType();
|
||||
return type;
|
||||
}
|
||||
|
||||
private static Class<?>[] getParamTypes(Class<?> factory,
|
||||
AbstractBeanDefinition definition) {
|
||||
if (definition instanceof RootBeanDefinition) {
|
||||
RootBeanDefinition root = (RootBeanDefinition) definition;
|
||||
for (Method method : getCandidateMethods(factory, root)) {
|
||||
if (root.isFactoryMethod(method)) {
|
||||
return method.getParameterTypes();
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Class<?>> params = new ArrayList<>();
|
||||
for (ConstructorArgumentValues.ValueHolder holder : definition
|
||||
.getConstructorArgumentValues().getIndexedArgumentValues().values()) {
|
||||
params.add(ClassUtils.resolveClassName(holder.getType(), null));
|
||||
}
|
||||
return params.toArray(new Class<?>[0]);
|
||||
}
|
||||
|
||||
private static Method[] getCandidateMethods(final Class<?> factoryClass,
|
||||
final RootBeanDefinition mbd) {
|
||||
if (System.getSecurityManager() != null) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
|
||||
@Override
|
||||
public Method[] run() {
|
||||
return (mbd.isNonPublicAccessAllowed()
|
||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||
: factoryClass.getMethods());
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
return (mbd.isNonPublicAccessAllowed()
|
||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||
: factoryClass.getMethods());
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getField(Object target, String name) {
|
||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation to be used on a Spring Boot application if it wants to deploy a jar file
|
||||
* containing a function definition.
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(FunctionDeployerConfiguration.class)
|
||||
public @interface EnableFunctionDeployer {
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.io.IOException;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
// @checkstyle:off
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableFunctionDeployer
|
||||
public class FunctionApplication {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
new ApplicationBootstrap().run(FunctionApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
// @checkstyle:on
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2019-2019 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
|
||||
*
|
||||
* https://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.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.loader.JarLauncher;
|
||||
import org.springframework.boot.loader.LaunchedURLClassLoader;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.jar.JarFile;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionTypeUtils;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
class FunctionArchiveDeployer extends JarLauncher {
|
||||
|
||||
private static Log logger = LogFactory.getLog(FunctionArchiveDeployer.class);
|
||||
|
||||
private final StandardEvaluationContext evalContext = new StandardEvaluationContext();
|
||||
|
||||
private LaunchedURLClassLoader archiveLoader;
|
||||
|
||||
FunctionArchiveDeployer(Archive archive) {
|
||||
super(archive);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
void deploy(FunctionRegistry functionRegistry, FunctionProperties functionProperties, String[] args) {
|
||||
ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(createClassLoader(discoverClassPathAcrhives()));
|
||||
evalContext.setTypeLocator(new StandardTypeLocator(Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
if (this.isBootApplicationWithMain()) {
|
||||
this.launchFunctionArchive(args);
|
||||
|
||||
Map<String, Object> functions = this.discoverBeanFunctions();
|
||||
if (logger.isInfoEnabled() && !CollectionUtils.isEmpty(functions)) {
|
||||
logger.info("Discovered functions in deployed application context: " + functions);
|
||||
}
|
||||
for (Entry<String, Object> entry : functions.entrySet()) {
|
||||
FunctionRegistration registration = new FunctionRegistration(entry.getValue(), entry.getKey());
|
||||
Type type = this.discoverFunctionType(entry.getKey());
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Registering function '" + entry.getKey() + "' of type '" + type
|
||||
+ "' in FunctionRegistry.");
|
||||
}
|
||||
registration.type(type);
|
||||
functionRegistry.register(registration);
|
||||
}
|
||||
}
|
||||
|
||||
String functionClassName = discoverFunctionClassName(functionProperties);
|
||||
if (!StringUtils.isEmpty(functionClassName)) {
|
||||
FunctionRegistration registration = this.discovereAndLoadFunctionFromClassName(functionClassName);
|
||||
if (registration != null) {
|
||||
functionRegistry.register(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to deploy archive " + this.getArchive(), e);
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(currentLoader);
|
||||
}
|
||||
}
|
||||
|
||||
void undeploy() {
|
||||
this.stopDeployedApplicationContext();
|
||||
try {
|
||||
this.archiveLoader.close();
|
||||
logger.info("Closed archive class loader");
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.error("Failed to closed archive class loader", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader createClassLoader(URL[] urls) throws Exception {
|
||||
String classAsPath = DeployerContextUtils.class.getName().replace('.', '/') + ".class";
|
||||
byte[] deployerContextUtilsBytes = StreamUtils
|
||||
.copyToByteArray(DeployerContextUtils.class.getClassLoader().getResourceAsStream(classAsPath));
|
||||
/*
|
||||
* While LaunchedURLClassLoader is completely disconnected with the current
|
||||
* class loader, this will ensure that certain classes (e.g., org.reactivestreams.* see #shouldLoadViaDeployerLoader() )
|
||||
* are shared across two class loaders.
|
||||
*/
|
||||
this.archiveLoader = new LaunchedURLClassLoader(urls, null) {
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
Class<?> clazz = null;
|
||||
if (shouldLoadViaDeployerLoader(name)) {
|
||||
try {
|
||||
clazz = getClass().getClassLoader().loadClass(name);
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Class '" + name + "' is not available in the current class loader. Loading it from the deployed archive.");
|
||||
}
|
||||
clazz = super.loadClass(name, false);
|
||||
}
|
||||
}
|
||||
else if (name.equals(DeployerContextUtils.class.getName())) {
|
||||
/*
|
||||
* This will ensure that `DeployerContextUtils` is available to
|
||||
* foreign class loader for cases where foreign JAR does not
|
||||
* have SCF dependencies.
|
||||
*/
|
||||
try {
|
||||
clazz = super.loadClass(name, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
clazz = defineClass(name, deployerContextUtilsBytes, 0, deployerContextUtilsBytes.length);
|
||||
}
|
||||
}
|
||||
else {
|
||||
clazz = super.loadClass(name, false);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
};
|
||||
return this.archiveLoader;
|
||||
}
|
||||
|
||||
private boolean shouldLoadViaDeployerLoader(String name) {
|
||||
return name.startsWith("org.reactivestreams")
|
||||
|| name.startsWith("reactor.")
|
||||
|| name.startsWith("java")
|
||||
|| name.startsWith("com.sun");
|
||||
}
|
||||
|
||||
private String discoverFunctionClassName(FunctionProperties functionProperties) {
|
||||
try {
|
||||
return StringUtils.hasText(functionProperties.getFunctionClass())
|
||||
? functionProperties.getFunctionClass()
|
||||
: this.getArchive().getManifest().getMainAttributes().getValue("Function-Class");
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to discover function class name", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBootApplicationWithMain() {
|
||||
try {
|
||||
return StringUtils.hasText(this.getArchive().getManifest().getMainAttributes().getValue("Start-Class"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Archive> discoverClassPathAcrhives() throws Exception {
|
||||
List<Archive> classPathArchives = getClassPathArchives();
|
||||
if (CollectionUtils.isEmpty(classPathArchives)) {
|
||||
classPathArchives.add(this.getArchive());
|
||||
}
|
||||
return classPathArchives;
|
||||
}
|
||||
|
||||
private FunctionRegistration<?> discovereAndLoadFunctionFromClassName(String functionClassName) throws Exception {
|
||||
FunctionRegistration<?> functionRegistration = null;
|
||||
AtomicReference<Type> typeRef = new AtomicReference<>();
|
||||
Class<?> functionClass = Thread.currentThread().getContextClassLoader().loadClass(functionClassName);
|
||||
|
||||
ReflectionUtils.doWithMethods(functionClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
typeRef.set(FunctionTypeUtils.getFunctionTypeFromFunctionMethod(method));
|
||||
}
|
||||
}, new MethodFilter() {
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
String name = method.getName();
|
||||
return typeRef.get() == null && !method.isBridge()
|
||||
&& ("apply".equals(name) || "accept".equals(name) || "get".equals(name));
|
||||
}
|
||||
});
|
||||
|
||||
if (typeRef.get() != null) {
|
||||
Object functionInstance = functionClass.newInstance();
|
||||
String functionName = StringUtils.uncapitalize(functionClass.getSimpleName());
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Registering function class '" + functionClass + "' of type '" + typeRef.get()
|
||||
+ "' under name '" + functionName + "'.");
|
||||
}
|
||||
functionRegistration = new FunctionRegistration<>(functionInstance, functionName);
|
||||
functionRegistration.type(typeRef.get());
|
||||
}
|
||||
return functionRegistration;
|
||||
}
|
||||
|
||||
private void launchFunctionArchive(String[] args) throws Exception {
|
||||
JarFile.registerUrlProtocolHandler();
|
||||
|
||||
String mainClassName = getMainClass();
|
||||
Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(mainClassName);
|
||||
|
||||
Class<?> bootAppClass = Thread.currentThread().getContextClassLoader()
|
||||
.loadClass(SpringApplication.class.getName());
|
||||
Method runMethod = bootAppClass.getDeclaredMethod("run", Class.class, String[].class);
|
||||
Object applicationContext = runMethod.invoke(null, mainClass, (Object) args);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Application context for archive '" + this.getArchive().getUrl() + "' is created.");
|
||||
}
|
||||
evalContext.setVariable("context", applicationContext);
|
||||
setBeanFactory(applicationContext);
|
||||
}
|
||||
|
||||
private void setBeanFactory(Object applicationContext) {
|
||||
Expression parsed = new SpelExpressionParser().parseExpression("#context.getBeanFactory()");
|
||||
Object beanFactory = parsed.getValue(this.evalContext);
|
||||
evalContext.setVariable("bf", beanFactory);
|
||||
}
|
||||
|
||||
private Type discoverFunctionType(String name) {
|
||||
evalContext.setVariable("functionName", name);
|
||||
String expr = "T(" + DeployerContextUtils.class.getName() + ").findType(#bf, #functionName)";
|
||||
Expression parsed = new SpelExpressionParser().parseExpression(expr);
|
||||
Object type = parsed.getValue(this.evalContext);
|
||||
return (Type) type;
|
||||
}
|
||||
|
||||
private void stopDeployedApplicationContext() {
|
||||
if (evalContext.lookupVariable("context") != null) { // no start-class uber jars
|
||||
Expression parsed = new SpelExpressionParser().parseExpression("#context.stop()");
|
||||
parsed.getValue(this.evalContext);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> discoverBeanFunctions() {
|
||||
Map<String, Object> allFunctions = new HashMap<String, Object>();
|
||||
if (evalContext.lookupVariable("context") != null) { // no start-class uber jars
|
||||
Expression parsed = new SpelExpressionParser()
|
||||
.parseExpression("#context.getBeansOfType(T(java.util.function.Function))");
|
||||
allFunctions.putAll((Map<String, Object>) parsed.getValue(this.evalContext));
|
||||
parsed = new SpelExpressionParser().parseExpression("#context.getBeansOfType(T(java.util.function.Supplier))");
|
||||
allFunctions.putAll((Map<String, Object>) parsed.getValue(this.evalContext));
|
||||
parsed = new SpelExpressionParser().parseExpression("#context.getBeansOfType(T(java.util.function.Consumer))");
|
||||
allFunctions.putAll((Map<String, Object>) parsed.getValue(this.evalContext));
|
||||
}
|
||||
return allFunctions;
|
||||
}
|
||||
}
|
||||
@@ -1,658 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.loader.JarLauncher;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
import org.springframework.boot.system.JavaVersion;
|
||||
import org.springframework.cloud.deployer.resource.support.DelegatingResourceLoader;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.cloud.function.context.FunctionType;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
||||
import org.springframework.cloud.function.core.FluxFunction;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* Registers beans that will be picked up by spring-cloud-function-context magic. Sets up
|
||||
* infrastructure capable of instantiating a "functional" bean (whether Supplier, Function
|
||||
* or Consumer) loaded dynamically according to {@link FunctionProperties}.
|
||||
*
|
||||
* <p>
|
||||
* Resolves jar location provided by the user using a flexible ResourceLoader.
|
||||
* </p>
|
||||
*
|
||||
* @author Eric Bottard
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
*/
|
||||
@Configuration
|
||||
class FunctionCreatorConfiguration {
|
||||
|
||||
private static Log logger = LogFactory.getLog(FunctionCreatorConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
private FunctionRegistry registry;
|
||||
|
||||
@Autowired
|
||||
private FunctionProperties properties;
|
||||
|
||||
@Autowired
|
||||
private DelegatingResourceLoader delegatingResourceLoader;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private BeanCreatorClassLoader functionClassLoader;
|
||||
|
||||
private BeanCreator creator;
|
||||
|
||||
/**
|
||||
* Registers a function for each of the function classes passed into the
|
||||
* {@link FunctionProperties}. They are named sequentially "function0", "function1",
|
||||
* etc. The instances are created in an isolated class loader, so the jar they are
|
||||
* packed in has to define all the dependencies (except core JDK).
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
URL[] urls = Arrays.stream(this.properties.getLocation())
|
||||
.flatMap(toResourceURL(this.delegatingResourceLoader))
|
||||
.toArray(URL[]::new);
|
||||
URL[] roots = Arrays.stream(this.properties.getLocation()).map(this::toUrl)
|
||||
.toArray(URL[]::new);
|
||||
|
||||
try {
|
||||
logger.info("Locating function from "
|
||||
+ Arrays.asList(this.properties.getLocation()));
|
||||
this.creator = new BeanCreator(roots, urls);
|
||||
this.creator.run(this.properties.getMain());
|
||||
Arrays.stream(functionNames()).map(this.creator::create).sequential()
|
||||
.forEach(this.creator::register);
|
||||
if (this.properties.getName().contains("|")) {
|
||||
// A composite function has to be explicitly registered before it is
|
||||
// looked up because we are using the SingleEntryFunctionRegistry
|
||||
// Object o = this.registry.lookup(Consumer.class, this.properties.getName());
|
||||
// o = this.registry.lookup(Function.class, this.properties.getName());
|
||||
// o = this.registry.lookup(Supplier.class, this.properties.getName());
|
||||
// System.out.println();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot create functions", e);
|
||||
}
|
||||
}
|
||||
|
||||
private URL toUrl(String url) {
|
||||
if (url.equals("app:classpath")) {
|
||||
return urls()[0];
|
||||
}
|
||||
try {
|
||||
return new URL(url);
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] functionNames() {
|
||||
if (this.properties.getBean() != null && this.properties.getBean().length > 0) {
|
||||
return this.properties.getBean();
|
||||
}
|
||||
return this.creator.getFunctionNames();
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.creator != null) {
|
||||
this.creator.close();
|
||||
}
|
||||
if (this.functionClassLoader != null) {
|
||||
try {
|
||||
this.functionClassLoader.close();
|
||||
this.functionClassLoader = null;
|
||||
Runtime.getRuntime().gc();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot close function class loader", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Function<String, Stream<URL>> toResourceURL(
|
||||
DelegatingResourceLoader resourceLoader) {
|
||||
return l -> {
|
||||
if (l.equals("app:classpath")) {
|
||||
return Stream.of(urls());
|
||||
}
|
||||
try {
|
||||
return Stream.of(resourceLoader.getResource(l).getFile().toURI().toURL());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private URL[] urls() {
|
||||
if (getClass().getClassLoader() instanceof URLClassLoader) {
|
||||
return ((URLClassLoader) getClass().getClassLoader()).getURLs();
|
||||
}
|
||||
// Want to load these the test types in a disposable classloader:
|
||||
List<URL> urls = new ArrayList<>();
|
||||
String jcp = System.getProperty("java.class.path");
|
||||
StringTokenizer jcpEntries = new StringTokenizer(jcp, File.pathSeparator);
|
||||
while (jcpEntries.hasMoreTokens()) {
|
||||
String pathEntry = jcpEntries.nextToken();
|
||||
try {
|
||||
urls.add(new File(pathEntry).toURI().toURL());
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
}
|
||||
}
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
|
||||
private static final class BeanCreatorClassLoader extends URLClassLoader {
|
||||
|
||||
private BeanCreatorClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super(urls, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
try {
|
||||
if (name.startsWith("javax.") && JavaVersion.getJavaVersion()
|
||||
.isEqualOrNewerThan(JavaVersion.NINE)) {
|
||||
return getClass().getClassLoader().loadClass(name);
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
if (name.contains(ContextRunner.class.getName())
|
||||
|| name.contains(PostConstruct.class.getName())) {
|
||||
// Special case for the ContextRunner. We can re-use the bytes for it,
|
||||
// and the function jar doesn't have to include them since it is only
|
||||
// used here.
|
||||
byte[] bytes;
|
||||
try {
|
||||
bytes = StreamUtils.copyToByteArray(
|
||||
getClass().getClassLoader().getResourceAsStream(
|
||||
ClassUtils.convertClassNameToResourcePath(name)
|
||||
+ ".class"));
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new ClassNotFoundException(
|
||||
"Cannot find runner class: " + name, ex);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class SingleEntryConfiguration implements BeanPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof FunctionRegistry) {
|
||||
String name = FunctionProperties
|
||||
.functionName(this.env.getProperty("function.bean", ""));
|
||||
if (name.contains("|")) {
|
||||
// A single composite function with an empty name
|
||||
bean = new SingleEntryFunctionRegistry((FunctionRegistry) bean, name);
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ComputeLauncher extends JarLauncher {
|
||||
|
||||
ComputeLauncher(Archive archive) {
|
||||
super(archive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMainClass() throws Exception {
|
||||
Manifest manifest = getArchive().getManifest();
|
||||
String mainClass = null;
|
||||
if (manifest != null) {
|
||||
String functionClass = manifest.getMainAttributes()
|
||||
.getValue("Function-Class");
|
||||
if (StringUtils.hasText(functionClass) && ObjectUtils.isEmpty(
|
||||
FunctionCreatorConfiguration.this.properties.getBean())) {
|
||||
FunctionCreatorConfiguration.this.properties
|
||||
.setBean(new String[] { functionClass });
|
||||
}
|
||||
mainClass = manifest.getMainAttributes().getValue("Start-Class");
|
||||
if (mainClass == null
|
||||
// Not surefire or IntelliJ
|
||||
&& !getArchive().getUrl().toString().endsWith(".jar!/")) {
|
||||
// Not a Spring Boot jar but it might have a "main" class
|
||||
mainClass = manifest.getMainAttributes().getValue("Main-Class");
|
||||
}
|
||||
}
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
public URL[] getClassLoaderUrls() throws Exception {
|
||||
List<Archive> archives = getClassPathArchives();
|
||||
if (archives.isEmpty()) {
|
||||
URL url = getArchive().getUrl();
|
||||
if (url.toString().contains(".jar")) { // Surefire or IntelliJ?
|
||||
URL[] classpath = extractClasspath(url.toString());
|
||||
if (classpath != null) {
|
||||
return classpath;
|
||||
}
|
||||
}
|
||||
return new URL[] { getArchive().getUrl() };
|
||||
}
|
||||
return archives.stream().map(archive -> {
|
||||
try {
|
||||
return archive.getUrl();
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new IllegalStateException("Bad URL: " + archive, e);
|
||||
}
|
||||
}).toArray(URL[]::new);
|
||||
}
|
||||
|
||||
private URL[] extractClasspath(String url) {
|
||||
// This works for a jar indirection like in surefire and IntelliJ
|
||||
if (url.endsWith(".jar!/")) {
|
||||
url = url.substring(0, url.length() - "!/".length());
|
||||
if (url.startsWith("jar:")) {
|
||||
url = url.substring("jar:".length());
|
||||
}
|
||||
if (url.startsWith("file:")) {
|
||||
url = url.substring("file:".length());
|
||||
}
|
||||
}
|
||||
if (url.endsWith(".jar")) {
|
||||
JarFile jar;
|
||||
try {
|
||||
jar = new JarFile(new File(url));
|
||||
String path = jar.getManifest().getMainAttributes()
|
||||
.getValue("Class-Path");
|
||||
if (path != null) {
|
||||
List<URL> result = new ArrayList<>();
|
||||
for (String element : path.split(" ")) {
|
||||
result.add(new URL(element));
|
||||
}
|
||||
return result.toArray(new URL[0]);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the bean and spring application context creation concerns for
|
||||
* functions. Creates a single application context if <code>run()</code> is called
|
||||
* with a non-null main class, and then uses it to lookup a function (by name and then
|
||||
* by type).
|
||||
*/
|
||||
private class BeanCreator {
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
private ApplicationRunner runner;
|
||||
|
||||
private String defaultMain;
|
||||
|
||||
BeanCreator(URL[] roots, URL[] urls) {
|
||||
FunctionCreatorConfiguration.this.functionClassLoader = new BeanCreatorClassLoader(
|
||||
expand(urls), getParent());
|
||||
this.defaultMain = findMain(roots);
|
||||
}
|
||||
|
||||
private ClassLoader getParent() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
loader = loader.getParent();
|
||||
ClassLoader parent = loader;
|
||||
while (loader.getParent() != null) {
|
||||
// If launched from a fat jar with spring.factories skip this parent level
|
||||
// (which was added by the JarLauncher).
|
||||
if (loader.getResource("META-INF/spring.factories") != null) {
|
||||
parent = loader.getParent();
|
||||
}
|
||||
loader = loader.getParent();
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
private String findMain(URL[] urls) {
|
||||
for (URL url : urls) {
|
||||
try {
|
||||
File file = ResourceUtils.getFile(url);
|
||||
if (file.exists()) {
|
||||
Archive archive = file.getName().endsWith(".jar")
|
||||
? new JarFileArchive(file) : new ExplodedArchive(file);
|
||||
String main = new ComputeLauncher(archive).getMainClass();
|
||||
if (main != null) {
|
||||
return main;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private URL[] expand(URL[] urls) {
|
||||
List<URL> result = new ArrayList<>();
|
||||
for (URL url : urls) {
|
||||
result.addAll(expand(url));
|
||||
}
|
||||
return result.toArray(new URL[0]);
|
||||
}
|
||||
|
||||
private List<URL> expand(URL url) {
|
||||
if (!"file".equals(url.getProtocol())) {
|
||||
return Collections.singletonList(url);
|
||||
}
|
||||
try {
|
||||
File file = new File(url.toURI());
|
||||
if (file.exists()) {
|
||||
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());
|
||||
}
|
||||
return Collections.singletonList(url);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Cannot create class loader for " + url,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public void run(String main) {
|
||||
if (main == null) {
|
||||
main = this.defaultMain;
|
||||
}
|
||||
if (main == null) {
|
||||
return;
|
||||
}
|
||||
if (ClassUtils.isPresent(SpringApplication.class.getName(),
|
||||
FunctionCreatorConfiguration.this.functionClassLoader)) {
|
||||
logger.info("SpringApplication available. Bootstrapping: " + main);
|
||||
ClassLoader contextClassLoader = ClassUtils
|
||||
.overrideThreadContextClassLoader(
|
||||
FunctionCreatorConfiguration.this.functionClassLoader);
|
||||
try {
|
||||
ApplicationRunner runner = new ApplicationRunner(
|
||||
FunctionCreatorConfiguration.this.functionClassLoader, main);
|
||||
// TODO: make the runtime properties configurable
|
||||
runner.run("--spring.main.webEnvironment=false",
|
||||
"--spring.cloud.stream.enabled=false",
|
||||
"--spring.main.bannerMode=OFF",
|
||||
"--spring.main.webApplicationType=none",
|
||||
"--function.deployer.enabled=false");
|
||||
this.runner = runner;
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(contextClassLoader);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"SpringApplication not available and main class requested: "
|
||||
+ main);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getFunctionNames() {
|
||||
Set<String> list = new LinkedHashSet<>();
|
||||
ClassLoader contextClassLoader = ClassUtils.overrideThreadContextClassLoader(
|
||||
FunctionCreatorConfiguration.this.functionClassLoader);
|
||||
try {
|
||||
if (this.runner.containsBean(FunctionCatalog.class.getName())) {
|
||||
Object catalog = this.runner.getBean(FunctionCatalog.class.getName());
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> functions = (Set<String>) this.runner
|
||||
.evaluate("getNames(#type)", catalog, "type", Function.class);
|
||||
list.addAll(functions);
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> consumers = (Set<String>) this.runner
|
||||
.evaluate("getNames(#type)", catalog, "type", Consumer.class);
|
||||
list.addAll(consumers);
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> suppliers = (Set<String>) this.runner
|
||||
.evaluate("getNames(#type)", catalog, "type", Supplier.class);
|
||||
list.addAll(suppliers);
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
list.addAll(this.runner.getBeanNames(Function.class.getName()));
|
||||
list.addAll(this.runner.getBeanNames(Consumer.class.getName()));
|
||||
list.addAll(this.runner.getBeanNames(Supplier.class.getName()));
|
||||
}
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(contextClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public Object create(String type) {
|
||||
ClassLoader contextClassLoader = ClassUtils.overrideThreadContextClassLoader(
|
||||
FunctionCreatorConfiguration.this.functionClassLoader);
|
||||
AutowireCapableBeanFactory factory = FunctionCreatorConfiguration.this.context
|
||||
.getAutowireCapableBeanFactory();
|
||||
try {
|
||||
Object result = null;
|
||||
if (this.runner != null) {
|
||||
result = this.runner.getBean(type);
|
||||
if (result == null) {
|
||||
if (this.runner.containsBean(FunctionCatalog.class.getName())) {
|
||||
Object catalog = this.runner
|
||||
.getBean(FunctionCatalog.class.getName());
|
||||
result = this.runner.evaluate("lookup(#function).getTarget()",
|
||||
catalog, "function", type);
|
||||
if (result != null) {
|
||||
logger.info("Located registration: " + type + " of type "
|
||||
+ result.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.info("Located bean: " + type + " of type "
|
||||
+ result.getClass());
|
||||
if (result.getClass().getName()
|
||||
.equals(FunctionRegistration.class.getName())) {
|
||||
result = this.runner.evaluate("getTarget()", result);
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
if (result.getClass().getName()
|
||||
.equals(FluxFunction.class.getName())) {
|
||||
result = this.runner.evaluate("getTarget()", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
logger.info("No bean found. Instantiating: " + type);
|
||||
if (ClassUtils.isPresent(type,
|
||||
FunctionCreatorConfiguration.this.functionClassLoader)) {
|
||||
result = factory.createBean(ClassUtils.resolveClassName(type,
|
||||
FunctionCreatorConfiguration.this.functionClassLoader));
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
throw new IllegalStateException("Cannot create bean for: " + type);
|
||||
}
|
||||
finally {
|
||||
ClassUtils.overrideThreadContextClassLoader(contextClassLoader);
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionType createFunctionType(Object functionCatalog) {
|
||||
FunctionType functionType = null;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
String name = ((Set<String>) this.runner.evaluate("getNames(#type)", functionCatalog, "type", null))
|
||||
.stream().findFirst().orElse(null);
|
||||
if (name != null) {
|
||||
Object ft = this.runner.evaluate("getFunctionType(#name)", functionCatalog, "name", name);
|
||||
Constructor<FunctionType> ftConstructor = FunctionType.class.getDeclaredConstructor(Object.class);
|
||||
ftConstructor.setAccessible(true);
|
||||
functionType = ftConstructor.newInstance(ft);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to extract and map FunctionType", e);
|
||||
|
||||
}
|
||||
return functionType;
|
||||
}
|
||||
|
||||
public void register(Object bean) {
|
||||
if (bean == null) {
|
||||
return;
|
||||
}
|
||||
FunctionRegistration<Object> registration = new FunctionRegistration<Object>(
|
||||
bean,
|
||||
FunctionProperties.functionName(this.counter.getAndIncrement()));
|
||||
if (this.runner != null) {
|
||||
if (this.runner.containsBean(FunctionInspector.class.getName())) {
|
||||
Object functionCatalog = this.runner
|
||||
.getBean(FunctionInspector.class.getName());
|
||||
|
||||
FunctionType type = this.createFunctionType(functionCatalog);
|
||||
if (type == null) {
|
||||
Class<?> input = (Class<?>) this.runner.evaluate(
|
||||
"getInputType(#function)", functionCatalog, "function", bean);
|
||||
type = FunctionType.from(input);
|
||||
Class<?> output = findType("getOutputType", functionCatalog, bean);
|
||||
type = type.to(output);
|
||||
if (((Boolean) this.runner.evaluate("isMessage(#function)", functionCatalog,
|
||||
"function", bean))) {
|
||||
type = type.message();
|
||||
}
|
||||
Class<?> inputWrapper = findType("getInputWrapper", functionCatalog, bean);
|
||||
if (FunctionType.isWrapper(inputWrapper)) {
|
||||
type = type.wrap(inputWrapper);
|
||||
}
|
||||
Class<?> outputWrapper = findType("getOutputWrapper", functionCatalog,
|
||||
bean);
|
||||
if (FunctionType.isWrapper(outputWrapper)) {
|
||||
type = type.wrap(outputWrapper);
|
||||
}
|
||||
}
|
||||
registration.type(this.createFunctionType(functionCatalog));
|
||||
}
|
||||
}
|
||||
else {
|
||||
registration.type(FunctionType.of(bean.getClass()).getType());
|
||||
}
|
||||
registration.target(bean);
|
||||
if (registration.getType() == null) {
|
||||
registration.type(FunctionType.of(bean.getClass()).getType());
|
||||
}
|
||||
FunctionCreatorConfiguration.this.registry.register(registration);
|
||||
}
|
||||
|
||||
private Class<?> findType(String method, Object inspector, Object bean) {
|
||||
return (Class<?>) this.runner.evaluate(method + "(#function)", inspector,
|
||||
"function", bean);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (this.runner != null) {
|
||||
this.runner.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
@@ -16,51 +16,80 @@
|
||||
|
||||
package org.springframework.cloud.function.deployer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenResource;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenResourceLoader;
|
||||
import org.springframework.cloud.deployer.resource.support.DelegatingResourceLoader;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "function.deployer", name = "enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties
|
||||
@Import(FunctionCreatorConfiguration.class)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(FunctionProperties.class)
|
||||
public class FunctionDeployerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("maven")
|
||||
public MavenProperties mavenProperties() {
|
||||
return new MavenProperties();
|
||||
}
|
||||
private static Log logger = LogFactory.getLog(FunctionDeployerConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("function")
|
||||
public FunctionProperties functionProperties() {
|
||||
return new FunctionProperties();
|
||||
}
|
||||
SmartLifecycle functionArchiveDeployer(FunctionProperties functionProperties,
|
||||
FunctionRegistry functionRegistry, ApplicationArguments arguments) {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DelegatingResourceLoader.class)
|
||||
public DelegatingResourceLoader delegatingResourceLoader(
|
||||
MavenProperties mavenProperties) {
|
||||
Map<String, ResourceLoader> loaders = new HashMap<>();
|
||||
loaders.put(MavenResource.URI_SCHEME, new MavenResourceLoader(mavenProperties));
|
||||
return new DelegatingResourceLoader(loaders);
|
||||
Archive archive = null;
|
||||
try {
|
||||
archive = new JarFileArchive(new File(functionProperties.getLocation()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to create archive: " + functionProperties.getLocation(), e);
|
||||
}
|
||||
FunctionArchiveDeployer deployer = new FunctionArchiveDeployer(archive);
|
||||
|
||||
return new SmartLifecycle() {
|
||||
|
||||
private boolean running;
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Undeploying archive: " + functionProperties.getLocation());
|
||||
}
|
||||
deployer.undeploy();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Successfully undeployed archive: " + functionProperties.getLocation());
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Deploying archive: " + functionProperties.getLocation());
|
||||
}
|
||||
deployer.deploy(functionRegistry, functionProperties, arguments.getSourceArgs());
|
||||
this.running = true;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Successfully deployed archive: " + functionProperties.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.running;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
@@ -16,11 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.function.deployer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -28,73 +27,52 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.function")
|
||||
public class FunctionProperties {
|
||||
|
||||
/**
|
||||
* Location(s) of jar archives containing the supplier/function/consumer class to run.
|
||||
* Location of jar archive containing the supplier/function/consumer class or bean to run.
|
||||
*/
|
||||
private String[] location = new String[0];
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* The bean name or fully qualified class name of the supplier/function/consumer to
|
||||
* run.
|
||||
* The name of the function to be looked up from the FunctionCatalog (e.g., bean name).
|
||||
*/
|
||||
private String[] bean = new String[0];
|
||||
private String functionName;
|
||||
|
||||
/**
|
||||
* Optional main class from which to build a Spring application context.
|
||||
* The name of the function class tyo be instantiated and loaded into FunctionCatalog. The name of the
|
||||
* function will be decapitalized simple name of this class.
|
||||
*/
|
||||
private String main;
|
||||
private String functionClass;
|
||||
|
||||
public static String functionName(String name) {
|
||||
if (!name.contains(",")) {
|
||||
return "function0";
|
||||
}
|
||||
List<String> names = new ArrayList<>();
|
||||
for (int i = 0; i <= StringUtils.countOccurrencesOf(name, ","); i++) {
|
||||
names.add("function" + i);
|
||||
}
|
||||
return StringUtils.collectionToDelimitedString(names, "|");
|
||||
public void setFunctionClass(String functionClass) {
|
||||
this.functionClass = functionClass;
|
||||
}
|
||||
|
||||
public static String functionName(int value) {
|
||||
return "function" + value;
|
||||
public String getFunctionClass() {
|
||||
return this.functionClass;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return functionName(StringUtils.arrayToDelimitedString(this.bean, ","));
|
||||
public void setFunctionName(String functionName) {
|
||||
this.functionName = StringUtils.hasText(functionName) ? functionName : "";
|
||||
}
|
||||
|
||||
public String[] getBean() {
|
||||
return this.bean;
|
||||
public String getFunctionName() {
|
||||
return this.functionName;
|
||||
}
|
||||
|
||||
public void setBean(String[] bean) {
|
||||
this.bean = bean;
|
||||
}
|
||||
|
||||
public String[] getLocation() {
|
||||
public String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
public void setLocation(String[] location) {
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String getMain() {
|
||||
return this.main;
|
||||
}
|
||||
|
||||
public void setMain(String main) {
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.location.length == 0) {
|
||||
throw new IllegalStateException(
|
||||
"No archive location provided, please configure function.location as a jar or directory.");
|
||||
}
|
||||
Assert.notNull(this.location, "No archive location provided, please configure spring.cloud.function.location as a jar or directory.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SingleEntryFunctionRegistry implements FunctionRegistry {
|
||||
|
||||
private final FunctionRegistry delegate;
|
||||
|
||||
private final String name;
|
||||
|
||||
public SingleEntryFunctionRegistry(FunctionRegistry delegate, String name) {
|
||||
this.delegate = delegate;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T lookup(Class<?> type, String name) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
if (this.delegate.getNames(type).size() == 1) {
|
||||
return this.delegate.lookup(type,
|
||||
this.delegate.getNames(type).iterator().next());
|
||||
}
|
||||
name = this.name;
|
||||
}
|
||||
return name.equals(this.name) ? this.delegate.lookup(type, name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getNames(Class<?> type) {
|
||||
return Collections.singleton(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void register(FunctionRegistration<T> registration) {
|
||||
this.delegate.register(registration);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.function.deployer.FunctionDeployerConfiguration
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 org.junit.Ignore;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
/**
|
||||
* A test suite for probing weird ordering problems in the tests.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({ FunctionCreatorConfigurationTests.FunctionCompositionTests.class,
|
||||
FunctionCreatorConfigurationTests.SingleFunctionTests.class,
|
||||
FunctionCreatorConfigurationTests.ManualSpringFunctionTests.class,
|
||||
ContextRunnerTests.class,
|
||||
SpringFunctionAppConfigurationTests.ProcessorTests.class,
|
||||
SpringFunctionAppConfigurationTests.SourceTests.class,
|
||||
FunctionCreatorConfigurationTests.ConsumerCompositionTests.class,
|
||||
SpringFunctionAppConfigurationTests.CompositeTests.class,
|
||||
ApplicationRunnerTests.class, SpringFunctionAppConfigurationTests.SinkTests.class,
|
||||
FunctionCreatorConfigurationTests.SupplierCompositionTests.class })
|
||||
@Ignore
|
||||
public class AdhocTestSuite {
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.test.Doubler;
|
||||
import org.springframework.cloud.function.test.FunctionApp;
|
||||
import org.springframework.cloud.function.test.FunctionRegistrar;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
|
||||
public class ApplicationRunnerTests {
|
||||
|
||||
@Test
|
||||
public void startEvaluateAndStop() {
|
||||
ApplicationRunner runner = new ApplicationRunner(getClass().getClassLoader(),
|
||||
FunctionApp.class.getName());
|
||||
runner.run("--spring.main.webEnvironment=false");
|
||||
assertThat(runner.containsBean(Doubler.class.getName())).isTrue();
|
||||
assertThat(runner.getBean(Doubler.class.getName())).isNotNull();
|
||||
runner.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void functional() {
|
||||
ApplicationRunner runner = new ApplicationRunner(getClass().getClassLoader(),
|
||||
FunctionRegistrar.class.getName());
|
||||
runner.run();
|
||||
assertThat(runner.containsBean(Doubler.class.getName())).isFalse();
|
||||
assertThat(runner.getBean(FunctionCatalog.class.getName())).isNotNull();
|
||||
assertThat(runner.getBeanNames(FunctionRegistration.class.getName())).hasSize(2);
|
||||
runner.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.function.test.Doubler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ContextRunnerTests {
|
||||
|
||||
@Test
|
||||
public void startEvaluateAndStop() {
|
||||
ContextRunner runner = new ContextRunner();
|
||||
runner.run(Doubler.class.getName(), Collections.emptyMap(),
|
||||
"--spring.main.webEnvironment=false");
|
||||
assertThat(runner.getContext()).isNotNull();
|
||||
runner.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
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.annotation.DirtiesContext;
|
||||
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;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = { FunctionDeployerConfiguration.class })
|
||||
@DirtiesContext
|
||||
public abstract class FunctionCreatorConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
protected FunctionCatalog catalog;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.location=file:target/test-classes",
|
||||
"function.bean=org.springframework.cloud.function.test.Doubler" })
|
||||
public static class SingleFunctionTests extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=app:classpath,file:target/test-classes,file:target/test-classes/app",
|
||||
"function.bean=myDoubler" })
|
||||
public static class SingleFunctionWithAutoMainTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=app:classpath,file:target/test-classes,file:target/test-classes/app",
|
||||
"function.bean=myDoubler",
|
||||
"function.main=org.springframework.cloud.function.test.FunctionApp" })
|
||||
public static class SingleFunctionWithMainTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=app:classpath,file:target/test-classes,file:target/test-classes/app",
|
||||
"function.bean=doubler",
|
||||
"function.main=org.springframework.cloud.function.test.FunctionRegistrar" })
|
||||
public static class SingleFunctionWithRegistrarTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=app:classpath,file:target/test-classes,file:target/test-classes/app",
|
||||
"function.bean=frenchizer",
|
||||
"function.main=org.springframework.cloud.function.test.FunctionRegistrar" })
|
||||
public static class SingleFunctionWithRegistrarAndRegistrationTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testFrenchize() {
|
||||
Function<Flux<Integer>, Flux<String>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo("deux");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=app:classpath,file:target/test-classes,file:target/test-classes/app",
|
||||
"function.bean=myDoubler",
|
||||
"function.main=org.springframework.cloud.function.test.FunctionInitializer" })
|
||||
@Ignore
|
||||
public static class SingleFunctionWithInitializerTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.location=app:classpath",
|
||||
"function.bean=org.springframework.cloud.function.test.SpringDoubler" })
|
||||
public static class ManualSpringFunctionTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testDouble() {
|
||||
Function<Flux<Integer>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.location=file:target/test-classes",
|
||||
"function.bean=org.springframework.cloud.function.test.NumberEmitter,"
|
||||
+ "org.springframework.cloud.function.test.Frenchizer" })
|
||||
@Ignore
|
||||
public static class SupplierCompositionTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testSupplier() {
|
||||
Supplier<Integer> function = this.catalog.lookup(Supplier.class, "function0");
|
||||
assertThat(function).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunction() {
|
||||
Supplier<String> function = this.catalog.lookup(Supplier.class,
|
||||
"function0|function1");
|
||||
assertThat(function.get()).isEqualTo("un");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.location=file:target/test-classes",
|
||||
"function.bean=org.springframework.cloud.function.test.Doubler,"
|
||||
+ "org.springframework.cloud.function.test.Frenchizer" })
|
||||
public static class FunctionCompositionTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void testFunction() {
|
||||
Function<Flux<Integer>, Flux<String>> function = this.catalog
|
||||
.lookup(Function.class, "function0|function1");
|
||||
assertThat(function.apply(Flux.just(2)).blockFirst()).isEqualTo("quatre");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThen() {
|
||||
Function<Integer, String> function = this.catalog.lookup(Function.class,
|
||||
"function1");
|
||||
assertThat(function).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.location=file:target/test-classes",
|
||||
"function.bean=org.springframework.cloud.function.test.Frenchizer,"
|
||||
+ "org.springframework.cloud.function.test.Printer" })
|
||||
public static class ConsumerCompositionTests
|
||||
extends FunctionCreatorConfigurationTests {
|
||||
|
||||
@Rule
|
||||
public OutputCapture capture = new OutputCapture();
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testConsumer() {
|
||||
Function<Flux<Integer>, Mono<Void>> function = this.catalog
|
||||
.lookup(Function.class, "function0|function1");
|
||||
function.apply(Flux.just(2)).block();
|
||||
this.capture.expect(containsString("Seen deux"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright 2017-2019 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
|
||||
*
|
||||
* https://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.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 3.0
|
||||
*/
|
||||
public class FunctionDeployerTests {
|
||||
|
||||
/*
|
||||
* Target function `class UpperCaseFunction implements Function<String, String>`
|
||||
* Main/Start class present, no Spring configuration
|
||||
*/
|
||||
@Test
|
||||
public void testWithMainAndStartClassNoSpringConfiguration() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootjar/target/bootjar-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-class=function.example.UpperCaseFunction" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("upperCaseFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
assertThat(function.apply("stacy")).isEqualTo("STACY");
|
||||
|
||||
Function<Flux<String>, Flux<String>> functionAsFlux = catalog.lookup("upperCaseFunction");
|
||||
|
||||
List<String> results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block();
|
||||
assertThat(results.get(0)).isEqualTo("BOB");
|
||||
assertThat(results.get(1)).isEqualTo("STACY");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithSimplestJar() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-0.0.1.BUILD-SNAPSHOT.jar",
|
||||
"--spring.cloud.function.function-class=function.example.UpperCaseFunction" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("upperCaseFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
assertThat(function.apply("stacy")).isEqualTo("STACY");
|
||||
|
||||
Function<Flux<String>, Flux<String>> functionAsFlux = catalog.lookup("upperCaseFunction");
|
||||
|
||||
List<String> results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block();
|
||||
assertThat(results.get(0)).isEqualTo("BOB");
|
||||
assertThat(results.get(1)).isEqualTo("STACY");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function `class UpperCaseFunction implements Function<String, String>`
|
||||
* No Main/Start class present, no Spring configuration
|
||||
*/
|
||||
@Test
|
||||
public void testNoMainAndNoStartClassAndNoSpringConfiguration() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootjarnostart/target/bootjarnostart-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-class=function.example.UpperCaseFunction" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("upperCaseFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
assertThat(function.apply("stacy")).isEqualTo("STACY");
|
||||
|
||||
Function<Flux<String>, Flux<String>> functionAsFlux = catalog.lookup("upperCaseFunction");
|
||||
|
||||
List<String> results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block();
|
||||
assertThat(results.get(0)).isEqualTo("BOB");
|
||||
assertThat(results.get(1)).isEqualTo("STACY");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function `class UpperCaseFunction implements Function<String, String>`
|
||||
* No Main/Start class present, no Spring configuration
|
||||
*
|
||||
* Function class is discovered via 'Function-Class` manifest entry
|
||||
*/
|
||||
@Test
|
||||
public void testNoMainAndNoStartClassAndNoSpringConfigurationDiscoverClassFromManifest() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootjarnostart/target/bootjarnostart-0.0.1.BUILD-SNAPSHOT-exec.jar" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<String, String> function = catalog.lookup("upperCaseFunction");
|
||||
|
||||
assertThat(function.apply("bob")).isEqualTo("BOB");
|
||||
assertThat(function.apply("stacy")).isEqualTo("STACY");
|
||||
|
||||
Function<Flux<String>, Flux<String>> functionAsFlux = catalog.lookup("upperCaseFunction");
|
||||
|
||||
List<String> results = functionAsFlux.apply(Flux.just("bob", "stacy")).collectList().block();
|
||||
assertThat(results.get(0)).isEqualTo("BOB");
|
||||
assertThat(results.get(1)).isEqualTo("STACY");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function:
|
||||
*
|
||||
* @Bean public Function<String, String> uppercase()
|
||||
*/
|
||||
@Test
|
||||
public void testWithMainAndStartClassAndSpringConfiguration() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootapp/target/bootapp-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-name=uppercase" };
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<Message<byte[]>, Message<byte[]>> function = catalog.lookup("uppercase", "application/json");
|
||||
|
||||
Message<byte[]> result = function
|
||||
.apply(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build());
|
||||
assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\"");
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as above but:
|
||||
* Given that Java 11 does not include 'javax' packages, this test simply validates that
|
||||
* the delegation will be made to archive loader where it is available
|
||||
*/
|
||||
@Test
|
||||
public void testWithMainAndStartClassAndSpringConfigurationJavax() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootapp-with-javax/target/bootapp-with-javax-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-name=uppercase" };
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<Message<byte[]>, Message<byte[]>> function = catalog.lookup("uppercase", "application/json");
|
||||
|
||||
Message<byte[]> result = function
|
||||
.apply(MessageBuilder.withPayload("\"foo@bar.com\"".getBytes(StandardCharsets.UTF_8)).build());
|
||||
assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"FOO@BAR.COM\"");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function:
|
||||
*
|
||||
* @Bean public Function<String, String> uppercase()
|
||||
*
|
||||
* this contains SCF on classpath
|
||||
*/
|
||||
@Test
|
||||
public void testWithMainAndStartClassAndSpringConfigurationAndSCFOnClasspath() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootapp-with-scf/target/bootapp-with-scf-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-name=uppercase" };
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<Message<byte[]>, Message<byte[]>> function = catalog.lookup("uppercase", "application/json");
|
||||
|
||||
Message<byte[]> result = function
|
||||
.apply(MessageBuilder.withPayload("\"bob\"".getBytes(StandardCharsets.UTF_8)).build());
|
||||
assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("\"BOB\"");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target function:
|
||||
*
|
||||
* @Bean public Function<Person, Person> uppercasePerson()
|
||||
*/
|
||||
@Test
|
||||
public void testWithMainAndStartClassAndSpringConfigurationAndTypeConversion() throws Exception {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootapp/target/bootapp-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-name=uppercasePerson" };
|
||||
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
Function<Message<byte[]>, Message<byte[]>> function = catalog.lookup("uppercasePerson", "application/json");
|
||||
|
||||
Message<byte[]> result = function.apply(
|
||||
MessageBuilder.withPayload("{\"name\":\"bob\",\"id\":1}".getBytes(StandardCharsets.UTF_8)).build());
|
||||
assertThat(new String(result.getPayload(), StandardCharsets.UTF_8)).isEqualTo("{\"name\":\"BOB\",\"id\":1}");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target Function
|
||||
*
|
||||
* @Bean Function<Tuple2<Flux<String>, Flux<Integer>>, Tuple2<Flux<Double>, Flux<String>>>
|
||||
*/
|
||||
@Test
|
||||
public void testBootAppWithMultipleInputOutput() {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootapp-multi/target/bootapp-multi-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-name=fn"
|
||||
};
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
|
||||
Function<Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>>, Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>>> function =
|
||||
catalog.lookup("fn", "application/json", "application/json");
|
||||
|
||||
Message<byte[]> msg1 = MessageBuilder.withPayload("\"one\"".getBytes()).build();
|
||||
Message<byte[]> msg2 = MessageBuilder.withPayload("\"two\"".getBytes()).build();
|
||||
Flux<Message<byte[]>> inputOne = Flux.just(msg1, msg2);
|
||||
|
||||
Message<byte[]> msgInt1 = MessageBuilder.withPayload("\"1\"".getBytes()).build();
|
||||
Message<byte[]> msgInt2 = MessageBuilder.withPayload("\"2\"".getBytes()).build();
|
||||
Flux<Message<byte[]>> inputTwo = Flux.just(msgInt1, msgInt2);
|
||||
|
||||
Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>> result = function.apply(Tuples.of(inputOne, inputTwo));
|
||||
List<String> result1 = new ArrayList<>();
|
||||
List<String> result2 = new ArrayList<>();
|
||||
result.getT1().subscribe(message -> {
|
||||
result1.add(new String(message.getPayload()));
|
||||
});
|
||||
result.getT2().subscribe(message -> {
|
||||
result2.add(new String(message.getPayload()));
|
||||
});
|
||||
|
||||
assertThat(result1.get(0)).isEqualTo("1.5");
|
||||
assertThat(result1.get(1)).isEqualTo("2.0");
|
||||
|
||||
assertThat(result2.get(0)).isEqualTo("\"one\"");
|
||||
assertThat(result2.get(1)).isEqualTo("\"two\"");
|
||||
}
|
||||
|
||||
/*
|
||||
* Target Function
|
||||
*
|
||||
* Function<Tuple2<Flux<String>, Flux<Integer>>, Tuple2<Flux<Double>, Flux<String>>>
|
||||
*/
|
||||
@Test
|
||||
public void testBootJarWithMultipleInputOutput() {
|
||||
String[] args = new String[] {
|
||||
"--spring.cloud.function.location=target/it/bootjar-multi/target/bootjar-multi-0.0.1.BUILD-SNAPSHOT-exec.jar",
|
||||
"--spring.cloud.function.function-class=function.example.Repeater"
|
||||
};
|
||||
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
|
||||
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
|
||||
|
||||
Function<Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>>, Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>>> function =
|
||||
catalog.lookup("repeater", "application/json", "application/json");
|
||||
|
||||
Message<byte[]> msg1 = MessageBuilder.withPayload("\"one\"".getBytes()).build();
|
||||
Message<byte[]> msg2 = MessageBuilder.withPayload("\"two\"".getBytes()).build();
|
||||
Flux<Message<byte[]>> inputOne = Flux.just(msg1, msg2);
|
||||
|
||||
Message<byte[]> msgInt1 = MessageBuilder.withPayload("\"1\"".getBytes()).build();
|
||||
Message<byte[]> msgInt2 = MessageBuilder.withPayload("\"2\"".getBytes()).build();
|
||||
Flux<Message<byte[]>> inputTwo = Flux.just(msgInt1, msgInt2);
|
||||
|
||||
Tuple2<Flux<Message<byte[]>>, Flux<Message<byte[]>>> result = function.apply(Tuples.of(inputOne, inputTwo));
|
||||
List<String> result1 = new ArrayList<>();
|
||||
List<String> result2 = new ArrayList<>();
|
||||
result.getT1().subscribe(message -> {
|
||||
result1.add(new String(message.getPayload()));
|
||||
});
|
||||
result.getT2().subscribe(message -> {
|
||||
result2.add(new String(message.getPayload()));
|
||||
});
|
||||
|
||||
assertThat(result1.get(0)).isEqualTo("\"one\"");
|
||||
assertThat(result1.get(1)).isEqualTo("\"two\"");
|
||||
|
||||
assertThat(result2.get(0)).isEqualTo("3");
|
||||
assertThat(result2.get(1)).isEqualTo("2");
|
||||
}
|
||||
|
||||
@SpringBootApplication(proxyBeanMethods = false)
|
||||
private static class DeployerApplication {
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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 org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.catalog.InMemoryFunctionCatalog;
|
||||
import org.springframework.cloud.function.core.FluxFunction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SingleEntryFunctionRegistryTests {
|
||||
|
||||
private InMemoryFunctionCatalog delegate = new InMemoryFunctionCatalog();
|
||||
|
||||
@Test
|
||||
public void named() {
|
||||
this.delegate.register(new FunctionRegistration<Foos>(new Foos(), "foo"));
|
||||
SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(
|
||||
this.delegate, "foo");
|
||||
assertThat(((FluxFunction<?, ?>) registry.lookup("")).getTarget()).isInstanceOf(Foos.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void other() {
|
||||
this.delegate.register(new FunctionRegistration<Foos>(new Foos(), "foo"));
|
||||
SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(
|
||||
this.delegate, "foo");
|
||||
assertThat(((FluxFunction<?, ?>) registry.lookup("")).getTarget()).isInstanceOf(Foos.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
this.delegate.register(new FunctionRegistration<Foos>(new Foos(), ""));
|
||||
SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(
|
||||
this.delegate, "");
|
||||
assertThat(((FluxFunction<?, ?>) registry.lookup("")).getTarget()).isInstanceOf(Foos.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anonymous() {
|
||||
this.delegate.register(new FunctionRegistration<Foos>(new Foos(), "bar"));
|
||||
SingleEntryFunctionRegistry registry = new SingleEntryFunctionRegistry(
|
||||
this.delegate, "foo");
|
||||
assertThat(((FluxFunction<?, ?>) registry.lookup("")).getTarget()).isInstanceOf(Foos.class);
|
||||
}
|
||||
|
||||
class Foos implements Function<String, String> {
|
||||
|
||||
@Override
|
||||
public String apply(String t) {
|
||||
return t.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = FunctionDeployerConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||
@TestPropertySource(properties = {
|
||||
"function.location=file:target/it/support/target/function-sample-1.0.0.M1-exec.jar" })
|
||||
public abstract class SpringFunctionAppConfigurationTests {
|
||||
|
||||
@Autowired
|
||||
protected FunctionCatalog catalog;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myEmitter",
|
||||
"function.main=com.example.functions.FunctionApp" })
|
||||
public static class SourceTests extends SpringFunctionAppConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Supplier<String> function = this.catalog.lookup(Supplier.class,
|
||||
"function0");
|
||||
assertThat(function.get()).isEqualTo("one");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myEmitter,myCounter" })
|
||||
public static class CompositeTests extends SpringFunctionAppConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Supplier<Integer> function = this.catalog.lookup(Supplier.class,
|
||||
"function0|function1");
|
||||
assertThat(function.get()).isEqualTo(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myCounter" })
|
||||
public static class ProcessorTests extends SpringFunctionAppConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Function<Flux<String>, Flux<Integer>> function = this.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 SpringFunctionAppConfigurationTests {
|
||||
|
||||
@Rule
|
||||
public OutputCapture capture = new OutputCapture();
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
// Can't assert side effects.
|
||||
Function<Flux<Integer>, Mono<Void>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
function.apply(Flux.just(5)).block();
|
||||
this.capture.expect(containsString(String.format("10%n")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
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;
|
||||
|
||||
@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
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Supplier<String> function = this.catalog.lookup(Supplier.class,
|
||||
"function0");
|
||||
assertThat(function.get()).isEqualTo("one");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myEmitter,myCounter" })
|
||||
public static class CompositeTests
|
||||
extends SpringFunctionAppExplodedConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Supplier<Integer> function = this.catalog.lookup(Supplier.class,
|
||||
"function0|function1");
|
||||
assertThat(function.get()).isEqualTo(3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myCounter" })
|
||||
public static class ProcessorTests
|
||||
extends SpringFunctionAppExplodedConfigurationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
Function<Flux<String>, Flux<Integer>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
assertThat(function.apply(Flux.just("spam")).blockFirst()).isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@TestPropertySource(properties = { "function.bean=myDoubler" })
|
||||
@Ignore // @TestPropertySource is not taken into account nor it is visible
|
||||
public static class SinkTests extends SpringFunctionAppExplodedConfigurationTests {
|
||||
|
||||
@Rule
|
||||
public OutputCapture capture = new OutputCapture();
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
// Can't assert side effects.
|
||||
Function<Flux<Integer>, Mono<Void>> function = this.catalog
|
||||
.lookup(Function.class, "function0");
|
||||
function.apply(Flux.just(5)).block();
|
||||
this.capture.expect(containsString(String.format("10%n")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.catalog.FunctionInspector;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@EnableFunctionDeployer
|
||||
public class SpringFunctionFluxConfigurationTests {
|
||||
|
||||
private Object catalog;
|
||||
|
||||
private Object inspector;
|
||||
|
||||
private ApplicationBootstrap bootstrap;
|
||||
|
||||
@Before
|
||||
public void run() {
|
||||
if (this.bootstrap == null) {
|
||||
this.bootstrap = new ApplicationBootstrap();
|
||||
this.bootstrap.run(SpringFunctionFluxConfigurationTests.class,
|
||||
"--function.location=file:target/it/flux/target/dependency",
|
||||
"--function.bean=foos",
|
||||
"--function.main=com.example.functions.FunctionApp");
|
||||
this.catalog = this.bootstrap.getRunner()
|
||||
.getBean(FunctionCatalog.class.getName());
|
||||
this.inspector = this.bootstrap.getRunner()
|
||||
.getBean(FunctionInspector.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.bootstrap != null) {
|
||||
this.bootstrap.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<Object, Object> function = (Function<Object, Object>) this.bootstrap
|
||||
.getRunner()
|
||||
.evaluate("lookup(T(java.util.function.Function), 'function0')",
|
||||
this.catalog);
|
||||
assertThat(function).isNotNull();
|
||||
Class<?> inputType = (Class<?>) this.bootstrap.getRunner().evaluate(
|
||||
"getInputType(#function)", this.inspector, "function", function);
|
||||
assertThat(inputType.getName()).isEqualTo("com.example.functions.Foo");
|
||||
Object foo = create(inputType);
|
||||
Class<?> outputType = (Class<?>) this.bootstrap.getRunner().evaluate(
|
||||
"getOutputType(#function)", this.inspector, "function", function);
|
||||
assertThat(outputType.getName()).isEqualTo("com.example.functions.Foo");
|
||||
String value = (String) this.bootstrap.getRunner().evaluate(
|
||||
"apply(T(reactor.core.publisher.Flux).just(#foo)).blockFirst().getValue()",
|
||||
function, "foo", foo);
|
||||
assertThat(value).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
private Object create(Class<?> inputType) throws InstantiationException,
|
||||
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
|
||||
Constructor<?> constructor = inputType.getConstructor(String.class);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance("foo");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Foo {
|
||||
|
||||
private String value;
|
||||
|
||||
Foo() {
|
||||
}
|
||||
|
||||
Foo(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Foo [value=" + this.value + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Doubler implements Function<Integer, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer apply(Integer integer) {
|
||||
return 2 * integer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
public class Frenchizer implements Function<Integer, String> {
|
||||
|
||||
private String[] numbers;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.numbers = new String[4];
|
||||
this.numbers[0] = "un";
|
||||
this.numbers[1] = "deux";
|
||||
this.numbers[2] = "trois";
|
||||
this.numbers[3] = "quatre";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(Integer integer) {
|
||||
if (integer < this.numbers.length + 1) {
|
||||
return this.numbers[integer - 1];
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SpringBootConfiguration
|
||||
public class FunctionApp {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(FunctionApp.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Doubler myDoubler() {
|
||||
return new Doubler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Frenchizer myFrenchizer() {
|
||||
return new Frenchizer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.function.context.FunctionalSpringApplication;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FunctionInitializer
|
||||
implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication application = new FunctionalSpringApplication(
|
||||
FunctionInitializer.class);
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Doubler myDoubler() {
|
||||
return new Doubler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Frenchizer myFrenchizer() {
|
||||
return new Frenchizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(GenericApplicationContext context) {
|
||||
// TODO: support for FunctionRegistration
|
||||
context.registerBean("myDoubler", Doubler.class, () -> myDoubler());
|
||||
context.registerBean("myFrenchizer", Frenchizer.class, () -> myFrenchizer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.function.context.FunctionRegistration;
|
||||
import org.springframework.cloud.function.context.FunctionType;
|
||||
import org.springframework.cloud.function.context.FunctionalSpringApplication;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FunctionRegistrar
|
||||
implements ApplicationContextInitializer<GenericApplicationContext> {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication application = new FunctionalSpringApplication(
|
||||
FunctionRegistrar.class);
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Doubler myDoubler() {
|
||||
return new Doubler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Frenchizer myFrenchizer() {
|
||||
return new Frenchizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(GenericApplicationContext context) {
|
||||
context.registerBean("theDoubler", FunctionRegistration.class,
|
||||
() -> new FunctionRegistration<>(myDoubler(), "doubler")
|
||||
.type(FunctionType.of((Doubler.class))));
|
||||
context.registerBean("frenchizer", FunctionRegistration.class, () -> {
|
||||
Frenchizer function = myFrenchizer();
|
||||
function.init();
|
||||
return new FunctionRegistration<>(function, "theFrenchizer")
|
||||
.type(FunctionType.of((Frenchizer.class)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class NumberEmitter implements Supplier<Integer> {
|
||||
|
||||
@Override
|
||||
public Integer get() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Printer implements Consumer<Object> {
|
||||
|
||||
@Override
|
||||
public void accept(Object o) {
|
||||
System.err.println("Seen " + o);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2019 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
|
||||
*
|
||||
* https://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.test;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.Banner.Mode;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
public class SpringDoubler implements Function<Integer, Integer> {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.context == null) {
|
||||
this.context = new SpringApplicationBuilder(FunctionApp.class)
|
||||
.bannerMode(Mode.OFF).registerShutdownHook(false)
|
||||
.web(WebApplicationType.NONE).run();
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer apply(Integer integer) {
|
||||
return 2 * integer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Implementation-Title: function-file-sample
|
||||
Implementation-Version: 1.0.0.M1
|
||||
Archiver-Version: Plexus Archiver
|
||||
Built-By: dsyer
|
||||
Implementation-Vendor-Id: com.example
|
||||
Spring-Boot-Version: 1.5.12.RELEASE
|
||||
Implementation-Vendor: Pivotal Software, Inc.
|
||||
Main-Class: org.springframework.boot.loader.JarLauncher
|
||||
Start-Class: org.springframework.cloud.function.test.FunctionApp
|
||||
Created-By: Apache Maven 3.5.0
|
||||
Build-Jdk: 1.8.0_131
|
||||
Implementation-URL: https://projects.spring.io/spring-boot/function-sam
|
||||
ple/
|
||||
Reference in New Issue
Block a user