Renamed deployer-new to deployer, removed old deployer

This commit is contained in:
Oleg Zhurakousky
2019-08-12 17:26:22 +02:00
parent 17ef359b60
commit ed2fa9bb7a
75 changed files with 462 additions and 463 deletions

View 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/

View 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();
}
}

Binary file not shown.

View 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

View File

@@ -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
View 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
View 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%

View File

@@ -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>

View 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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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-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>

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View 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-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>

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View 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>

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View 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>

View File

@@ -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);
}
}

View 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>

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View 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>

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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 + "]";
}
}

View 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>

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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];
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
};
}
}

View File

@@ -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.");
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.function.deployer.FunctionDeployerConfiguration

View File

@@ -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 {
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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"));
}
}
}

View File

@@ -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 {
}
}

View File

@@ -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();
}
}
}

View File

@@ -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")));
}
}
}

View File

@@ -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")));
}
}
}

View File

@@ -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 + "]";
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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)));
});
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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/