Initial Commit
Migrating the existing structure from the following location: https://github.com/spring-cloud-stream-app-starters/stream-applications/tree/restructuring
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
apps/
|
||||
/application.yml
|
||||
/application.properties
|
||||
asciidoctor.css
|
||||
*~
|
||||
.#*
|
||||
*#
|
||||
target/
|
||||
build/
|
||||
bin/
|
||||
_site/
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.DS_Store
|
||||
*.sw*
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
.factorypath
|
||||
spring-xd-samples/*/xd
|
||||
dump.rdb
|
||||
coverage-error.log
|
||||
.apt_generated
|
||||
aws.credentials.properties
|
||||
1
.mvn/jvm.config
Normal file
1
.mvn/jvm.config
Normal file
@@ -0,0 +1 @@
|
||||
-Xmx1024m -XX:CICompilerCount=1 -XX:TieredStopAtLevel=1 -Djava.security.egd=file:/dev/./urandom
|
||||
1
.mvn/maven.config
Normal file
1
.mvn/maven.config
Normal file
@@ -0,0 +1 @@
|
||||
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring
|
||||
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
117
.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2007-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.5";
|
||||
/**
|
||||
* 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/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".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 directory '" + 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 {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar
|
||||
1
README.adoc
Normal file
1
README.adoc
Normal file
@@ -0,0 +1 @@
|
||||
== Stream applications
|
||||
81
applications/apps-core/.settings.xml
Normal file
81
applications/apps-core/.settings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<settings>
|
||||
<servers>
|
||||
<server>
|
||||
<id>repo.spring.io</id>
|
||||
<username>${env.CI_DEPLOY_USERNAME}</username>
|
||||
<password>${env.CI_DEPLOY_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
<profiles>
|
||||
<profile>
|
||||
<!--
|
||||
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
||||
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
||||
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
||||
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
||||
-->
|
||||
<id>spring</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</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>
|
||||
<!-- Gemstone repository is needed since gemfire libs
|
||||
are not hosted in mvn central. Note that the libs
|
||||
are also hosted and browsable at
|
||||
https://repo.spring.io/gemstone-release-cache -->
|
||||
<repository>
|
||||
<id>gemstone-release</id>
|
||||
<name>GemStone Maven Release Repository</name>
|
||||
<url>http://dist.gemstone.com/maven/release</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>always</updatePolicy>
|
||||
</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>
|
||||
</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>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
||||
44
applications/apps-core/CODE_OF_CONDUCT.adoc
Normal file
44
applications/apps-core/CODE_OF_CONDUCT.adoc
Normal file
@@ -0,0 +1,44 @@
|
||||
= Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open
|
||||
and welcoming community, we pledge to respect all people who contribute through reporting
|
||||
issues, posting feature requests, updating documentation, submitting pull requests or
|
||||
patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this
|
||||
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
|
||||
that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
|
||||
consistently applying these principles to every aspect of managing this project. Project
|
||||
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
|
||||
from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an
|
||||
individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
|
||||
be reviewed and investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
|
||||
with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the
|
||||
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
||||
201
applications/apps-core/LICENSE
Normal file
201
applications/apps-core/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
2
applications/apps-core/README.adoc
Normal file
2
applications/apps-core/README.adoc
Normal file
@@ -0,0 +1,2 @@
|
||||
#Core components shared by other projects in the app starters organization
|
||||
This module consists of core dependencies and other common artifacts.
|
||||
25
applications/apps-core/common/pom.xml
Normal file
25
applications/apps-core/common/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-parent</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>stream-apps-common</name>
|
||||
|
||||
<modules>
|
||||
<module>stream-apps-file-common</module>
|
||||
<module>stream-apps-ftp-common</module>
|
||||
<module>stream-apps-test-support</module>
|
||||
<module>stream-apps-postprocessor-common</module>
|
||||
<module>stream-apps-micrometer-common</module>
|
||||
<module>stream-apps-metadata-store-common</module>
|
||||
<module>stream-apps-task-launch-request-common</module>
|
||||
<module>stream-apps-security-common</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-file-common</artifactId>
|
||||
<name>stream-apps-file-common</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-file</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.file;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ConfigurationProperties("file.consumer")
|
||||
@Validated
|
||||
public class FileConsumerProperties {
|
||||
|
||||
/**
|
||||
* The FileReadingMode to use for file reading sources.
|
||||
* Values are 'ref' - The File object,
|
||||
* 'lines' - a message per line, or
|
||||
* 'contents' - the contents as bytes.
|
||||
*/
|
||||
private FileReadingMode mode = FileReadingMode.contents;
|
||||
|
||||
/**
|
||||
* Set to true to emit start of file/end of file marker messages before/after the data.
|
||||
* Only valid with FileReadingMode 'lines'.
|
||||
*/
|
||||
private Boolean withMarkers = null;
|
||||
|
||||
/**
|
||||
* When 'fileMarkers == true', specify if they should be produced
|
||||
* as FileSplitter.FileMarker objects or JSON.
|
||||
*/
|
||||
private boolean markersJson = true;
|
||||
|
||||
@NotNull
|
||||
public FileReadingMode getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public void setMode(FileReadingMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public Boolean getWithMarkers() {
|
||||
return this.withMarkers;
|
||||
}
|
||||
|
||||
public void setWithMarkers(Boolean withMarkers) {
|
||||
this.withMarkers = withMarkers;
|
||||
}
|
||||
|
||||
public boolean getMarkersJson() {
|
||||
return this.markersJson;
|
||||
}
|
||||
|
||||
public void setMarkersJson(boolean markersJson) {
|
||||
this.markersJson = markersJson;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "withMarkers can only be supplied when FileReadingMode is 'lines'")
|
||||
public boolean isWithMarkersValid() {
|
||||
return this.withMarkers == null || FileReadingMode.lines == this.mode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2015 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.stream.app.file;
|
||||
|
||||
|
||||
/**
|
||||
* Defines the supported modes of reading and processing files for the
|
||||
* {@code File}, {@code FTP} and {@code SFTP} sources.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author David Turanski
|
||||
*/
|
||||
public enum FileReadingMode {
|
||||
ref,
|
||||
lines,
|
||||
contents;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2015-2018 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.stream.app.file;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.integration.dsl.IntegrationFlowBuilder;
|
||||
import org.springframework.integration.file.splitter.FileSplitter;
|
||||
import org.springframework.integration.file.transformer.FileToByteArrayTransformer;
|
||||
import org.springframework.integration.transformer.StreamTransformer;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Christian Tzolov
|
||||
*
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* Enhance an {@link IntegrationFlowBuilder} to add flow snippets, depending on
|
||||
* {@link FileConsumerProperties}.
|
||||
* @param flowBuilder the flow builder.
|
||||
* @param fileConsumerProperties the properties.
|
||||
* @return the updated flow builder.
|
||||
*/
|
||||
public static IntegrationFlowBuilder enhanceFlowForReadingMode(IntegrationFlowBuilder flowBuilder,
|
||||
FileConsumerProperties fileConsumerProperties) {
|
||||
switch (fileConsumerProperties.getMode()) {
|
||||
case contents:
|
||||
flowBuilder.enrichHeaders(Collections.<String, Object>singletonMap(MessageHeaders.CONTENT_TYPE,
|
||||
MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE))
|
||||
.transform(new FileToByteArrayTransformer());
|
||||
break;
|
||||
case lines:
|
||||
Boolean withMarkers = fileConsumerProperties.getWithMarkers();
|
||||
if (withMarkers == null) {
|
||||
withMarkers = false;
|
||||
}
|
||||
flowBuilder.enrichHeaders(Collections.<String, Object>singletonMap(MessageHeaders.CONTENT_TYPE,
|
||||
MimeTypeUtils.TEXT_PLAIN_VALUE))
|
||||
.split(new FileSplitter(true, withMarkers, fileConsumerProperties.getMarkersJson()));
|
||||
break;
|
||||
case ref:
|
||||
flowBuilder.enrichHeaders(Collections.<String, Object>singletonMap(MessageHeaders.CONTENT_TYPE,
|
||||
MimeTypeUtils.APPLICATION_JSON_VALUE));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(fileConsumerProperties.getMode().name() +
|
||||
" is not a supported file reading mode.");
|
||||
}
|
||||
return flowBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhance an {@link IntegrationFlowBuilder} to add flow snippets, depending on
|
||||
* {@link FileConsumerProperties}; used for streaming sources.
|
||||
* @param flowBuilder the flow builder.
|
||||
* @param fileConsumerProperties the properties.
|
||||
* @return the updated flow builder.
|
||||
*/
|
||||
public static IntegrationFlowBuilder enhanceStreamFlowForReadingMode(IntegrationFlowBuilder flowBuilder,
|
||||
FileConsumerProperties fileConsumerProperties) {
|
||||
switch (fileConsumerProperties.getMode()) {
|
||||
case contents:
|
||||
flowBuilder.enrichHeaders(Collections.<String, Object>singletonMap(MessageHeaders.CONTENT_TYPE,
|
||||
MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE))
|
||||
.transform(new StreamTransformer());
|
||||
break;
|
||||
case lines:
|
||||
Boolean withMarkers = fileConsumerProperties.getWithMarkers();
|
||||
if (withMarkers == null) {
|
||||
withMarkers = false;
|
||||
}
|
||||
flowBuilder.enrichHeaders(Collections.<String, Object>singletonMap(MessageHeaders.CONTENT_TYPE,
|
||||
MimeTypeUtils.TEXT_PLAIN_VALUE))
|
||||
.split(new FileSplitter(true, withMarkers, fileConsumerProperties.getMarkersJson()));
|
||||
break;
|
||||
case ref:
|
||||
default:
|
||||
throw new IllegalArgumentException(fileConsumerProperties.getMode().name() +
|
||||
" is not a supported file reading mode when streaming.");
|
||||
}
|
||||
return flowBuilder;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.stream.app.file.remote;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @deprecated - properties are flattened.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractRemoteFileProperties {
|
||||
|
||||
/**
|
||||
* The remote FTP directory.
|
||||
*/
|
||||
private String remoteDir = "/";
|
||||
|
||||
/**
|
||||
* The suffix to use while the transfer is in progress.
|
||||
*/
|
||||
private String tmpFileSuffix = ".tmp";
|
||||
|
||||
/**
|
||||
* The remote file separator.
|
||||
*/
|
||||
private String remoteFileSeparator = "/";
|
||||
|
||||
@NotBlank
|
||||
public String getRemoteDir() {
|
||||
return remoteDir;
|
||||
}
|
||||
|
||||
public final void setRemoteDir(String remoteDir) {
|
||||
this.remoteDir = remoteDir;
|
||||
}
|
||||
|
||||
@NotBlank
|
||||
public String getTmpFileSuffix() {
|
||||
return tmpFileSuffix;
|
||||
}
|
||||
|
||||
public void setTmpFileSuffix(String tmpFileSuffix) {
|
||||
this.tmpFileSuffix = tmpFileSuffix;
|
||||
}
|
||||
|
||||
@NotBlank
|
||||
public String getRemoteFileSeparator() {
|
||||
return remoteFileSeparator;
|
||||
}
|
||||
|
||||
public void setRemoteFileSeparator(String remoteFileSeparator) {
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.file.remote;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.file.support.FileExistsMode;
|
||||
|
||||
/**
|
||||
* @deprecated - properties are flattened.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractRemoteFileSinkProperties extends AbstractRemoteFileProperties {
|
||||
|
||||
/**
|
||||
* A temporary directory where the file will be written if {@link #isUseTemporaryFilename()}
|
||||
* is true.
|
||||
*/
|
||||
private String temporaryRemoteDir = "/";
|
||||
|
||||
/**
|
||||
* Whether or not to create the remote directory.
|
||||
*/
|
||||
private boolean autoCreateDir = true;
|
||||
|
||||
/**
|
||||
* Action to take if the remote file already exists.
|
||||
*/
|
||||
private FileExistsMode mode = FileExistsMode.REPLACE;
|
||||
|
||||
/**
|
||||
* Whether or not to write to a temporary file and rename.
|
||||
*/
|
||||
private boolean useTemporaryFilename = true;
|
||||
|
||||
/**
|
||||
* A SpEL expression to generate the remote file name.
|
||||
*/
|
||||
private Expression filenameExpression;
|
||||
|
||||
@NotBlank
|
||||
public String getTemporaryRemoteDir() {
|
||||
return this.temporaryRemoteDir;
|
||||
}
|
||||
|
||||
public void setTemporaryRemoteDir(String temporaryRemoteDir) {
|
||||
this.temporaryRemoteDir = temporaryRemoteDir;
|
||||
}
|
||||
|
||||
public boolean isAutoCreateDir() {
|
||||
return this.autoCreateDir;
|
||||
}
|
||||
|
||||
public void setAutoCreateDir(boolean autoCreateDir) {
|
||||
this.autoCreateDir = autoCreateDir;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FileExistsMode getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public void setMode(FileExistsMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public boolean isUseTemporaryFilename() {
|
||||
return this.useTemporaryFilename;
|
||||
}
|
||||
|
||||
public void setUseTemporaryFilename(boolean useTemporaryFilename) {
|
||||
this.useTemporaryFilename = useTemporaryFilename;
|
||||
}
|
||||
|
||||
public Expression getFilenameExpression() {
|
||||
return this.filenameExpression;
|
||||
}
|
||||
|
||||
public void setFilenameExpression(Expression filenameExpression) {
|
||||
this.filenameExpression = filenameExpression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.stream.app.file.remote;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* Common properties for remote file sources (e.g. (S)FTP).
|
||||
*
|
||||
* @deprecated - properties are flattened.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractRemoteFileSourceProperties extends AbstractRemoteFileProperties {
|
||||
|
||||
/**
|
||||
* Set to true to delete remote files after successful transfer.
|
||||
*/
|
||||
private boolean deleteRemoteFiles = false;
|
||||
|
||||
/**
|
||||
* The local directory to use for file transfers.
|
||||
*/
|
||||
private File localDir = new File(System.getProperty("java.io.tmpdir") + "/xd/ftp");
|
||||
|
||||
/**
|
||||
* Set to true to create the local directory if it does not exist.
|
||||
*/
|
||||
private boolean autoCreateLocalDir = true;
|
||||
|
||||
/**
|
||||
* A filter pattern to match the names of files to transfer.
|
||||
*/
|
||||
private String filenamePattern;
|
||||
|
||||
/**
|
||||
* A filter regex pattern to match the names of files to transfer.
|
||||
*/
|
||||
private Pattern filenameRegex;
|
||||
|
||||
/**
|
||||
* Set to true to preserve the original timestamp.
|
||||
*/
|
||||
private boolean preserveTimestamp = true;
|
||||
|
||||
public boolean isAutoCreateLocalDir() {
|
||||
return autoCreateLocalDir;
|
||||
}
|
||||
|
||||
public void setAutoCreateLocalDir(boolean autoCreateLocalDir) {
|
||||
this.autoCreateLocalDir = autoCreateLocalDir;
|
||||
}
|
||||
|
||||
public boolean isDeleteRemoteFiles() {
|
||||
return deleteRemoteFiles;
|
||||
}
|
||||
|
||||
public void setDeleteRemoteFiles(boolean deleteRemoteFiles) {
|
||||
this.deleteRemoteFiles = deleteRemoteFiles;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public File getLocalDir() {
|
||||
return localDir;
|
||||
}
|
||||
|
||||
public final void setLocalDir(File localDir) {
|
||||
this.localDir = localDir;
|
||||
}
|
||||
|
||||
public String getFilenamePattern() {
|
||||
return filenamePattern;
|
||||
}
|
||||
|
||||
public void setFilenamePattern(String filenamePattern) {
|
||||
this.filenamePattern = filenamePattern;
|
||||
}
|
||||
|
||||
public Pattern getFilenameRegex() {
|
||||
return filenameRegex;
|
||||
}
|
||||
|
||||
public void setFilenameRegex(Pattern filenameRegex) {
|
||||
this.filenameRegex = filenameRegex;
|
||||
}
|
||||
|
||||
public boolean isPreserveTimestamp() {
|
||||
return preserveTimestamp;
|
||||
}
|
||||
|
||||
public void setPreserveTimestamp(boolean preserveTimestamp) {
|
||||
this.preserveTimestamp = preserveTimestamp;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "filenamePattern and filenameRegex are mutually exclusive")
|
||||
public boolean isExclusivePatterns() {
|
||||
return !(this.filenamePattern != null && this.filenameRegex != null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.file.remote;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* Common properties for remote servers (e.g. (S)FTP).
|
||||
*
|
||||
* @deprecated - properties are flattened.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractRemoteServerProperties {
|
||||
|
||||
/**
|
||||
* The host name of the server.
|
||||
*/
|
||||
private String host = "localhost";
|
||||
|
||||
/**
|
||||
* The username to use to connect to the server.
|
||||
*/
|
||||
|
||||
private String username;
|
||||
/**
|
||||
* The password to use to connect to the server.
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Cache sessions
|
||||
*/
|
||||
private Boolean cacheSessions;
|
||||
|
||||
@NotBlank
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@NotBlank
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Boolean getCacheSessions() {
|
||||
return this.cacheSessions;
|
||||
}
|
||||
|
||||
public void setCacheSessions(Boolean cacheSessions) {
|
||||
this.cacheSessions = cacheSessions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.file.remote;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
**/
|
||||
public abstract class FilePathUtils {
|
||||
/**
|
||||
* Returns a remote file path for a message with a file name as payload and {@link FileHeaders#REMOTE_DIRECTORY}
|
||||
* included as a message header.
|
||||
*
|
||||
* @param message the message containing the header.
|
||||
* @return the file path.
|
||||
*/
|
||||
@Nullable
|
||||
public static String getRemoteFilePath(Message message) {
|
||||
if (message.getHeaders().containsKey(FileHeaders.REMOTE_DIRECTORY)) {
|
||||
String filename = (String) message.getPayload();
|
||||
String remoteDirectory = (String) message.getHeaders().get(FileHeaders.REMOTE_DIRECTORY);
|
||||
return getPath(remoteDirectory, filename);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getLocalFilePath(String localDirectory, String filename) {
|
||||
if (localDirectory != null) {
|
||||
return getPath(localDirectory, filename);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
private static String getPath(String dirName, String fileName) {
|
||||
return Paths.get(dirName, fileName).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.stream.app.file.remote;
|
||||
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.remote.RemoteFileTemplate;
|
||||
import org.springframework.integration.transaction.IntegrationResourceHolder;
|
||||
import org.springframework.integration.transaction.TransactionSynchronizationProcessor;
|
||||
|
||||
/**
|
||||
* A {@link TransactionSynchronizationProcessor} that deletes a remote file on
|
||||
* success.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class RemoteFileDeletingTransactionSynchronizationProcessor implements TransactionSynchronizationProcessor {
|
||||
|
||||
private final RemoteFileTemplate<?> template;
|
||||
|
||||
private final String remoteFileSeparator;
|
||||
|
||||
/**
|
||||
* Construct an instance with the provided template and separator.
|
||||
* @param template the template.
|
||||
* @param remoteFileSeparator the separator.
|
||||
*/
|
||||
public RemoteFileDeletingTransactionSynchronizationProcessor(RemoteFileTemplate<?> template,
|
||||
String remoteFileSeparator) {
|
||||
this.template = template;
|
||||
this.remoteFileSeparator = remoteFileSeparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBeforeCommit(IntegrationResourceHolder holder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processAfterRollback(IntegrationResourceHolder holder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processAfterCommit(IntegrationResourceHolder holder) {
|
||||
String remoteDir = (String) holder.getMessage().getHeaders().get(FileHeaders.REMOTE_DIRECTORY);
|
||||
String remoteFile = (String) holder.getMessage().getHeaders().get(FileHeaders.REMOTE_FILE);
|
||||
this.template.remove(remoteDir + this.remoteFileSeparator + remoteFile);
|
||||
}
|
||||
|
||||
}
|
||||
26
applications/apps-core/common/stream-apps-ftp-common/pom.xml
Normal file
26
applications/apps-core/common/stream-apps-ftp-common/pom.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-ftp-common</artifactId>
|
||||
<name>stream-apps-ftp-common</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-ftp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-file-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
|
||||
|
||||
/**
|
||||
* FTP Session factory configuration.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(FtpSessionFactoryProperties.class)
|
||||
public class FtpSessionFactoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SessionFactory<FTPFile> ftpSessionFactory(FtpSessionFactoryProperties properties) {
|
||||
DefaultFtpSessionFactory ftpSessionFactory = new DefaultFtpSessionFactory();
|
||||
ftpSessionFactory.setHost(properties.getHost());
|
||||
ftpSessionFactory.setPort(properties.getPort());
|
||||
ftpSessionFactory.setUsername(properties.getUsername());
|
||||
ftpSessionFactory.setPassword(properties.getPassword());
|
||||
ftpSessionFactory.setClientMode(properties.getClientMode().getMode());
|
||||
if (properties.getCacheSessions() != null) {
|
||||
CachingSessionFactory<FTPFile> csf = new CachingSessionFactory<>(ftpSessionFactory);
|
||||
return csf;
|
||||
}
|
||||
else {
|
||||
return ftpSessionFactory;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.ftp;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.stream.app.file.remote.AbstractRemoteServerProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* FTP {@code SessionFactory} properties.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ConfigurationProperties("ftp.factory")
|
||||
@Validated
|
||||
public class FtpSessionFactoryProperties extends AbstractRemoteServerProperties {
|
||||
|
||||
/**
|
||||
* The port of the server.
|
||||
*/
|
||||
private int port = 21;
|
||||
|
||||
/**
|
||||
* The client mode to use for the FTP session.
|
||||
*/
|
||||
private ClientMode clientMode = ClientMode.PASSIVE;
|
||||
|
||||
@Range(min = 0, max = 65535)
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ClientMode getClientMode() {
|
||||
return this.clientMode;
|
||||
}
|
||||
|
||||
public void setClientMode(ClientMode clientMode) {
|
||||
this.clientMode = clientMode;
|
||||
}
|
||||
|
||||
public static enum ClientMode {
|
||||
|
||||
ACTIVE(FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE),
|
||||
PASSIVE(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE);
|
||||
|
||||
private final int mode;
|
||||
|
||||
private ClientMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
=== `MetadataStore` Common Module
|
||||
|
||||
This artifact contains a Spring Boot auto-configuration for the `MetadataStore`which can be used in various Spring Integration scenarios, like file polling, idempotent receiver, offset management etc.
|
||||
See Spring Integration "`https://docs.spring.io/spring-integration/docs/5.0.6.RELEASE/reference/html/system-management-chapter.html#metadata-store[Reference Manual]`" for more information.
|
||||
|
||||
In addition to the standard Spring Boot configuration properties this module exposes a `MetadataStoreProperties` with the `metadata.store` prefix.
|
||||
|
||||
To auto-configure particular `MetadataStore` you just need to bring respective dependencies into the target app starter:
|
||||
|
||||
==== Redis
|
||||
|
||||
The `RedisMetadataStore` requires regular Spring Boot auto-configuration for https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-redis[Spring Data Redis] and minimal set of dependencies is like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Additional configuration property for `RedisMetadataStore` is:
|
||||
|
||||
$$metadata.store.redis.key$$:: $$Redis key for metadata.$$ *($$String$$, default: `$$MetaData$$`)*
|
||||
|
||||
==== MongoDb
|
||||
|
||||
The `MongoDbMetadataStore` requires regular Spring Boot auto-configuration for https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-mongodb[Spring Data MongoDB] and minimal set of dependencies is like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-mongodb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Additional configuration property for `MongoDbMetadataStore` is:
|
||||
|
||||
$$metadata.store.mongo-db.collection$$:: $$MongoDB collection name for metadata.$$ *($$String$$, default: `$$metadataStore$$`)*
|
||||
|
||||
==== Pivotal Gemfire / Apache Geode
|
||||
|
||||
The `GemfireMetadataStore` requires these dependencies for auto-configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-gemfire</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
or when your environment is based on the Open Source https://geode.apache.org/[Apache Geode]:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-gemfire</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-gemfire</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-geode</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
You also can consider to use https://github.com/spring-projects/spring-boot-data-geode[Spring Boot Data Geode] instead for automatic dependency management and proper Spring Boot auto-configuration for Pivotal Gemfire/Apache Geode.
|
||||
|
||||
Additional configuration property for `GemfireMetadataStore` is:
|
||||
|
||||
$$metadata.store.gemfire.region$$:: $$Gemfire region name for metadata.$$ *($$String$$, default: `$$MetaData$$`)*
|
||||
|
||||
In addition, for the `GemfireMetadataStore`, a `MetadataStoreListener` bean can be configured in the application context to react to the `MetadataStore` events.
|
||||
|
||||
A default auto-configured `ClientRegionFactoryBean`, based on the auto-configured `GemFireCache`, bean can be overridden in the target application.
|
||||
|
||||
==== Hazelcast
|
||||
|
||||
The `HazelcastMetadataStore` requires regular Spring Boot auto-configuration for https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-caching-provider-hazelcast[Hazelcast] and minimal set of dependencies is like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-hazelcast</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
There are no additional configuration properties for the `HazelcastMetadataStore`, however a `MetadataStoreListener` bean can be configured in the application context to react to the `MetadataStore` events.
|
||||
|
||||
==== Zookeeper
|
||||
|
||||
The `ZookeeperMetadataStore` requires this dependency for auto-configuration:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-zookeeper</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
The configuration properties for `ZookeeperMetadataStore` are:
|
||||
|
||||
$$metadata.store.zookeeper.connect-string$$:: $$Zookeeper connect string in form HOST:PORT.$$ *($$String$$, default: `$$127.0.0.1:2181$$`)*
|
||||
$$metadata.store.zookeeper.retry-interval$$:: $$Retry interval for Zookeeper operations in milliseconds.$$ *($$int$$, default: `$$1000$$`)*
|
||||
$$metadata.store.zookeeper.encoding$$:: $$Encoding to use when storing data in Zookeeper.$$ *($$Charset$$, default: `$$UTF-8$$`)*
|
||||
$$metadata.store.zookeeper.root$$:: $$Root node - store entries are children of this node.$$ *($$String$$, default: `$$/SpringIntegration-MetadataStore$$`)*
|
||||
|
||||
In addition, for the `ZookeeperMetadataStore`, a `MetadataStoreListener` bean can be configured in the application context to react to the `MetadataStore` events.
|
||||
Also a `CuratorFramework` bean can be provided to override a default auto-configured one.
|
||||
|
||||
==== AWS DymanoDb
|
||||
|
||||
The `DynamoDbMetadataStore` requires regular Spring Cloud AWS auto-configuration for https://cloud.spring.io/spring-cloud-static/spring-cloud-aws/2.0.0.RELEASE/single/spring-cloud-aws.html#_spring_boot_auto_configuration[Spring Boot] and minimal set of dependencies is like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-aws</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-dynamodb</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Additional configuration properties for `DynamoDbMetadataStore` are:
|
||||
|
||||
$$metadata.store.dynamo-db.table:: $$Table name for metadata.$$ *($$String$$, default: `$$SpringIntegrationMetadataStore$$`)*
|
||||
$$metadata.store.dynamo-db.read-capacity:: $$Read capacity on the table.$$ *($$long$$, default: `$$1$$`)*
|
||||
$$metadata.store.dynamo-db.write-capacity:: $$Write capacity on the table.$$ *($$long$$, default: `$$1$$`)*
|
||||
$$metadata.store.dynamo-db.create-delay:: $$Delay between create table retries.$$ *($$int$$, default: `$$1$$`)*
|
||||
$$metadata.store.dynamo-db.create-retries:: $$Retry number for create table request.$$ *($$int$$, default: `$$25$$`)*
|
||||
$$metadata.store.dynamo-db.time-to-live:: $$TTL for table entries.$$ *($$Integer$$, default: `$$<none>$$`)*
|
||||
|
||||
A default, auto-configured `AmazonDynamoDBAsync` bean can be overridden in the target application.
|
||||
|
||||
==== JDBC
|
||||
|
||||
The `JdbcMetadataStore` requires regular Spring Boot auto-configuration for https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-sql[JDBC DataSource] and minimal set of dependencies is like this:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Plus vendor-specific JDBC driver artifact(s).
|
||||
|
||||
Additional configuration properties for `JdbcMetadataStore` are:
|
||||
|
||||
$$metadata.store.jdbc.table-prefix:: $$Prefix for the custom table name.$$ *($$String$$, default: `$$INT_$$`)*
|
||||
$$metadata.store.jdbc.region:: $$Unique grouping identifier for messages persisted with this store.$$ *($$String$$, default: `$$DEFAULT$$`)*
|
||||
|
||||
|
||||
|
||||
When no any of those technologies dependencies are preset, an in-memory `SimpleMetadataStore` is auto-configured.
|
||||
The target application can also provide its own `MetadataStore` bean to override any auto-configuration hooks.
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>stream-apps-metadata-store-common</artifactId>
|
||||
<name>stream-apps-metadata-store-common</name>
|
||||
|
||||
<properties>
|
||||
<aws-java-sdk.version>1.11.439</aws-java-sdk.version>
|
||||
<spring-integration-aws.version>2.0.0.RELEASE</spring-integration-aws.version>
|
||||
<spring-integration-hazelcast.version>1.0.0.RELEASE</spring-integration-hazelcast.version>
|
||||
<curator.version>4.0.1</curator.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--Redis-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--MongoDB-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-mongodb</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Gemfire-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-to-slf4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-gemfire</artifactId>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-gemfire</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!--TODO-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-geode</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>geode-spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>-->
|
||||
|
||||
<!--JDBC-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Zookeeper-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-zookeeper</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-test</artifactId>
|
||||
<version>${curator.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Hazelcast-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-hazelcast</artifactId>
|
||||
<version>${spring-integration-hazelcast.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--DynamoDB-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-aws</artifactId>
|
||||
<version>${spring-integration-aws.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-dynamodb</artifactId>
|
||||
<version>${aws-java-sdk.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.metadata;
|
||||
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.client.ClientCache;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
|
||||
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
|
||||
import org.springframework.data.gemfire.config.annotation.EnablePdx;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* This is the copy of {@code org.springframework.boot.data.geode.autoconfigure.ClientCacheAutoConfiguration}
|
||||
* until {@code geode-spring-boot-starter} is released.
|
||||
*
|
||||
* @author John Blum
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class })
|
||||
@ConditionalOnMissingBean(GemFireCache.class)
|
||||
@ClientCacheApplication
|
||||
@EnablePdx
|
||||
public class ClientCacheAutoConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.metadata;
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.retry.RetryForever;
|
||||
import org.apache.geode.cache.GemFireCache;
|
||||
import org.apache.geode.cache.Region;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.aws.core.region.RegionProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.integration.aws.metadata.DynamoDbMetadataStore;
|
||||
import org.springframework.integration.gemfire.metadata.GemfireMetadataStore;
|
||||
import org.springframework.integration.hazelcast.metadata.HazelcastMetadataStore;
|
||||
import org.springframework.integration.jdbc.metadata.JdbcMetadataStore;
|
||||
import org.springframework.integration.metadata.ConcurrentMetadataStore;
|
||||
import org.springframework.integration.metadata.MetadataStoreListener;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.mongodb.metadata.MongoDbMetadataStore;
|
||||
import org.springframework.integration.redis.metadata.RedisMetadataStore;
|
||||
import org.springframework.integration.zookeeper.metadata.ZookeeperMetadataStore;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync;
|
||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsyncClientBuilder;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0.2
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(ConcurrentMetadataStore.class)
|
||||
@EnableConfigurationProperties(MetadataStoreProperties.class)
|
||||
public class MetadataStoreAutoConfiguration {
|
||||
|
||||
@ConditionalOnClass(RedisMetadataStore.class)
|
||||
@ConditionalOnBean(RedisTemplate.class)
|
||||
static class Redis {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore redisMetadataStore(RedisTemplate<String, ?> redisTemplate,
|
||||
MetadataStoreProperties metadataStoreProperties) {
|
||||
|
||||
return new RedisMetadataStore(redisTemplate, metadataStoreProperties.getRedis().getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(MongoDbMetadataStore.class)
|
||||
@ConditionalOnBean(MongoTemplate.class)
|
||||
static class Mongo {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore mongoDbMetadataStore(MongoTemplate mongoTemplate,
|
||||
MetadataStoreProperties metadataStoreProperties) {
|
||||
|
||||
return new MongoDbMetadataStore(mongoTemplate, metadataStoreProperties.getMongoDb().getCollection());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(GemfireMetadataStore.class)
|
||||
@Import(ClientCacheAutoConfiguration.class)
|
||||
static class Gemfire {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientRegionFactoryBean<?, ?> gemfireRegion(GemFireCache cache,
|
||||
MetadataStoreProperties metadataStoreProperties) {
|
||||
|
||||
ClientRegionFactoryBean<?, ?> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
|
||||
clientRegionFactoryBean.setCache(cache);
|
||||
clientRegionFactoryBean.setName(metadataStoreProperties.getGemfire().getRegion());
|
||||
return clientRegionFactoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore gemfireMetadataStore(Region<?, ?> region,
|
||||
ObjectProvider<MetadataStoreListener> metadataStoreListenerObjectProvider) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
GemfireMetadataStore gemfireMetadataStore = new GemfireMetadataStore((Region<String, String>) region);
|
||||
metadataStoreListenerObjectProvider.ifAvailable(gemfireMetadataStore::addListener);
|
||||
|
||||
return gemfireMetadataStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(HazelcastMetadataStore.class)
|
||||
static class Hazelcast {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HazelcastInstance hazelcastInstance() {
|
||||
return com.hazelcast.core.Hazelcast.newHazelcastInstance();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore hazelcastMetadataStore(HazelcastInstance hazelcastInstance,
|
||||
ObjectProvider<MetadataStoreListener> metadataStoreListenerObjectProvider) {
|
||||
|
||||
HazelcastMetadataStore hazelcastMetadataStore = new HazelcastMetadataStore(hazelcastInstance);
|
||||
metadataStoreListenerObjectProvider.ifAvailable(hazelcastMetadataStore::addListener);
|
||||
return hazelcastMetadataStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass({ ZookeeperMetadataStore.class, CuratorFramework.class })
|
||||
static class Zookeeper {
|
||||
|
||||
@Bean(initMethod = "start")
|
||||
@ConditionalOnMissingBean
|
||||
public CuratorFramework curatorFramework(MetadataStoreProperties metadataStoreProperties) {
|
||||
MetadataStoreProperties.Zookeeper zookeeperProperties = metadataStoreProperties.getZookeeper();
|
||||
return CuratorFrameworkFactory.newClient(zookeeperProperties.getConnectString(),
|
||||
new RetryForever(zookeeperProperties.getRetryInterval()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore zookeeperMetadataStore(CuratorFramework curatorFramework,
|
||||
MetadataStoreProperties metadataStoreProperties,
|
||||
ObjectProvider<MetadataStoreListener> metadataStoreListenerObjectProvider) {
|
||||
|
||||
MetadataStoreProperties.Zookeeper zookeeperProperties = metadataStoreProperties.getZookeeper();
|
||||
ZookeeperMetadataStore zookeeperMetadataStore = new ZookeeperMetadataStore(curatorFramework);
|
||||
zookeeperMetadataStore.setEncoding(zookeeperProperties.getEncoding().name());
|
||||
zookeeperMetadataStore.setRoot(zookeeperProperties.getRoot());
|
||||
metadataStoreListenerObjectProvider.ifAvailable(zookeeperMetadataStore::addListener);
|
||||
return zookeeperMetadataStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(DynamoDbMetadataStore.class)
|
||||
@ConditionalOnBean({ AWSCredentialsProvider.class, RegionProvider.class })
|
||||
static class DynamoDb {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AmazonDynamoDBAsync dynamoDB(AWSCredentialsProvider awsCredentialsProvider,
|
||||
RegionProvider regionProvider) {
|
||||
|
||||
return AmazonDynamoDBAsyncClientBuilder.standard()
|
||||
.withCredentials(awsCredentialsProvider)
|
||||
.withRegion(
|
||||
regionProvider.getRegion()
|
||||
.getName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore dynamoDbMetadataStore(AmazonDynamoDBAsync dynamoDB,
|
||||
MetadataStoreProperties metadataStoreProperties) {
|
||||
|
||||
MetadataStoreProperties.DynamoDb dynamoDbProperties = metadataStoreProperties.getDynamoDb();
|
||||
|
||||
DynamoDbMetadataStore dynamoDbMetadataStore =
|
||||
new DynamoDbMetadataStore(dynamoDB, dynamoDbProperties.getTable());
|
||||
|
||||
dynamoDbMetadataStore.setReadCapacity(dynamoDbProperties.getReadCapacity());
|
||||
dynamoDbMetadataStore.setWriteCapacity(dynamoDbProperties.getWriteCapacity());
|
||||
dynamoDbMetadataStore.setCreateTableDelay(dynamoDbProperties.getCreateDelay());
|
||||
dynamoDbMetadataStore.setCreateTableRetries(dynamoDbProperties.getCreateRetries());
|
||||
if (dynamoDbProperties.getTimeToLive() != null) {
|
||||
dynamoDbMetadataStore.setTimeToLive(dynamoDbProperties.getTimeToLive());
|
||||
}
|
||||
|
||||
return dynamoDbMetadataStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(JdbcMetadataStore.class)
|
||||
@ConditionalOnBean(JdbcTemplate.class)
|
||||
static class Jdbc {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore jdbcMetadataStore(JdbcTemplate jdbcTemplate,
|
||||
MetadataStoreProperties metadataStoreProperties) {
|
||||
|
||||
MetadataStoreProperties.Jdbc jdbcProperties = metadataStoreProperties.getJdbc();
|
||||
|
||||
JdbcMetadataStore jdbcMetadataStore = new JdbcMetadataStore(jdbcTemplate);
|
||||
jdbcMetadataStore.setTablePrefix(jdbcProperties.getTablePrefix());
|
||||
jdbcMetadataStore.setRegion(jdbcProperties.getRegion());
|
||||
|
||||
return jdbcMetadataStore;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConcurrentMetadataStore simpleMetadataStore() {
|
||||
return new SimpleMetadataStore();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.metadata;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.integration.aws.metadata.DynamoDbMetadataStore;
|
||||
import org.springframework.integration.gemfire.metadata.GemfireMetadataStore;
|
||||
import org.springframework.integration.jdbc.metadata.JdbcMetadataStore;
|
||||
import org.springframework.integration.redis.metadata.RedisMetadataStore;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0.2
|
||||
*/
|
||||
@ConfigurationProperties("metadata.store")
|
||||
public class MetadataStoreProperties {
|
||||
|
||||
private final Mongo mongoDb = new Mongo();
|
||||
|
||||
private final Gemfire gemfire = new Gemfire();
|
||||
|
||||
private final Redis redis = new Redis();
|
||||
|
||||
private final DynamoDb dynamoDb = new DynamoDb();
|
||||
|
||||
private final Jdbc jdbc = new Jdbc();
|
||||
|
||||
private final Zookeeper zookeeper = new Zookeeper();
|
||||
|
||||
public Mongo getMongoDb() {
|
||||
return this.mongoDb;
|
||||
}
|
||||
|
||||
public Gemfire getGemfire() {
|
||||
return this.gemfire;
|
||||
}
|
||||
|
||||
public Redis getRedis() {
|
||||
return this.redis;
|
||||
}
|
||||
|
||||
public DynamoDb getDynamoDb() {
|
||||
return this.dynamoDb;
|
||||
}
|
||||
|
||||
public Jdbc getJdbc() {
|
||||
return this.jdbc;
|
||||
}
|
||||
|
||||
public Zookeeper getZookeeper() {
|
||||
return this.zookeeper;
|
||||
}
|
||||
|
||||
public static class Mongo {
|
||||
|
||||
/**
|
||||
* MongoDB collection name for metadata.
|
||||
*/
|
||||
private String collection = "metadataStore";
|
||||
|
||||
public String getCollection() {
|
||||
return this.collection;
|
||||
}
|
||||
|
||||
public void setCollection(String collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Gemfire {
|
||||
|
||||
/**
|
||||
* Gemfire region name for metadata.
|
||||
*/
|
||||
private String region = GemfireMetadataStore.KEY;
|
||||
|
||||
public String getRegion() {
|
||||
return this.region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Redis {
|
||||
|
||||
/**
|
||||
* Redis key for metadata.
|
||||
*/
|
||||
private String key = RedisMetadataStore.KEY;
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DynamoDb {
|
||||
|
||||
/**
|
||||
* Table name for metadata.
|
||||
*/
|
||||
private String table = DynamoDbMetadataStore.DEFAULT_TABLE_NAME;
|
||||
|
||||
/**
|
||||
* Read capacity on the table.
|
||||
*/
|
||||
private long readCapacity = 1L;
|
||||
|
||||
/**
|
||||
* Write capacity on the table.
|
||||
*/
|
||||
private long writeCapacity = 1L;
|
||||
|
||||
/**
|
||||
* Delay between create table retries.
|
||||
*/
|
||||
private int createDelay = 1;
|
||||
|
||||
/**
|
||||
* Retry number for create table request.
|
||||
*/
|
||||
private int createRetries = 25;
|
||||
|
||||
/**
|
||||
* TTL for table entries.
|
||||
*/
|
||||
private Integer timeToLive;
|
||||
|
||||
public String getTable() {
|
||||
return this.table;
|
||||
}
|
||||
|
||||
public void setTable(String table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public long getReadCapacity() {
|
||||
return this.readCapacity;
|
||||
}
|
||||
|
||||
public void setReadCapacity(long readCapacity) {
|
||||
this.readCapacity = readCapacity;
|
||||
}
|
||||
|
||||
public long getWriteCapacity() {
|
||||
return this.writeCapacity;
|
||||
}
|
||||
|
||||
public void setWriteCapacity(long writeCapacity) {
|
||||
this.writeCapacity = writeCapacity;
|
||||
}
|
||||
|
||||
public int getCreateDelay() {
|
||||
return this.createDelay;
|
||||
}
|
||||
|
||||
public void setCreateDelay(int createDelay) {
|
||||
this.createDelay = createDelay;
|
||||
}
|
||||
|
||||
public int getCreateRetries() {
|
||||
return this.createRetries;
|
||||
}
|
||||
|
||||
public void setCreateRetries(int createRetries) {
|
||||
this.createRetries = createRetries;
|
||||
}
|
||||
|
||||
public Integer getTimeToLive() {
|
||||
return this.timeToLive;
|
||||
}
|
||||
|
||||
public void setTimeToLive(Integer timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Jdbc {
|
||||
|
||||
/**
|
||||
* Prefix for the custom table name.
|
||||
*/
|
||||
private String tablePrefix = JdbcMetadataStore.DEFAULT_TABLE_PREFIX;
|
||||
|
||||
/**
|
||||
* Unique grouping identifier for messages persisted with this store.
|
||||
*/
|
||||
private String region = "DEFAULT";
|
||||
|
||||
public String getTablePrefix() {
|
||||
return this.tablePrefix;
|
||||
}
|
||||
|
||||
public void setTablePrefix(String tablePrefix) {
|
||||
this.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return this.region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Zookeeper {
|
||||
|
||||
/**
|
||||
* Zookeeper connect string in form HOST:PORT.
|
||||
*/
|
||||
private String connectString = "127.0.0.1:2181";
|
||||
|
||||
/**
|
||||
* Retry interval for Zookeeper operations in milliseconds.
|
||||
*/
|
||||
private int retryInterval = 1000;
|
||||
|
||||
/**
|
||||
* Encoding to use when storing data in Zookeeper.
|
||||
*/
|
||||
private Charset encoding = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Root node - store entries are children of this node.
|
||||
*/
|
||||
private String root = "/SpringIntegration-MetadataStore";
|
||||
|
||||
public String getConnectString() {
|
||||
return connectString;
|
||||
}
|
||||
|
||||
public void setConnectString(String connectString) {
|
||||
this.connectString = connectString;
|
||||
}
|
||||
|
||||
public int getRetryInterval() {
|
||||
return this.retryInterval;
|
||||
}
|
||||
|
||||
public void setRetryInterval(int retryInterval) {
|
||||
this.retryInterval = retryInterval;
|
||||
}
|
||||
|
||||
public Charset getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(Charset encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public String getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
public void setRoot(String root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.stream.app.metadata.MetadataStoreAutoConfiguration
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.metadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.willReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.curator.test.TestingServer;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.aws.core.region.RegionProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.aws.metadata.DynamoDbMetadataStore;
|
||||
import org.springframework.integration.gemfire.metadata.GemfireMetadataStore;
|
||||
import org.springframework.integration.hazelcast.metadata.HazelcastMetadataStore;
|
||||
import org.springframework.integration.jdbc.metadata.JdbcMetadataStore;
|
||||
import org.springframework.integration.metadata.ConcurrentMetadataStore;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.mongodb.metadata.MongoDbMetadataStore;
|
||||
import org.springframework.integration.redis.metadata.RedisMetadataStore;
|
||||
import org.springframework.integration.zookeeper.metadata.ZookeeperMetadataStore;
|
||||
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync;
|
||||
import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest;
|
||||
import com.amazonaws.services.dynamodbv2.model.DescribeTableResult;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0.2
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
@Ignore
|
||||
public class MetadataStoreAutoConfigurationTests {
|
||||
|
||||
private final static List<Class<? extends ConcurrentMetadataStore>> METADATA_STORE_CLASSES =
|
||||
Arrays.asList(
|
||||
RedisMetadataStore.class,
|
||||
MongoDbMetadataStore.class,
|
||||
GemfireMetadataStore.class,
|
||||
JdbcMetadataStore.class,
|
||||
ZookeeperMetadataStore.class,
|
||||
HazelcastMetadataStore.class,
|
||||
DynamoDbMetadataStore.class,
|
||||
SimpleMetadataStore.class
|
||||
);
|
||||
|
||||
private static FilteredClassLoader filteredClassLoaderBut(Class<? extends ConcurrentMetadataStore> classToInclude) {
|
||||
return new FilteredClassLoader(
|
||||
METADATA_STORE_CLASSES.stream()
|
||||
.filter(Predicate.isEqual(classToInclude).negate())
|
||||
.toArray(Class<?>[]::new));
|
||||
}
|
||||
|
||||
private final ApplicationContextRunner contextRunner;
|
||||
|
||||
private final Class<? extends ConcurrentMetadataStore> classToInclude;
|
||||
|
||||
|
||||
public MetadataStoreAutoConfigurationTests(Class<? extends ConcurrentMetadataStore> classToInclude) {
|
||||
this.classToInclude = classToInclude;
|
||||
this.contextRunner =
|
||||
new ApplicationContextRunner()
|
||||
.withUserConfiguration(TestConfiguration.class)
|
||||
.withClassLoader(filteredClassLoaderBut(classToInclude));
|
||||
}
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Iterable<?> parameters() {
|
||||
return METADATA_STORE_CLASSES;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetadataStore() {
|
||||
this.contextRunner
|
||||
.run(context -> {
|
||||
assertThat(context.getBeansOfType(MetadataStore.class)).hasSize(1);
|
||||
|
||||
assertThat(context.getBeanNamesForType(this.classToInclude))
|
||||
.containsOnlyOnce(Introspector.decapitalize(this.classToInclude.getSimpleName()));
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public static class TestConfiguration {
|
||||
|
||||
@Bean(destroyMethod = "stop")
|
||||
@ConditionalOnClass(ZookeeperMetadataStore.class)
|
||||
public static TestingServer zookeeperTestingServer() throws Exception {
|
||||
TestingServer testingServer = new TestingServer(true);
|
||||
|
||||
System.setProperty("metadata.store.zookeeper.connect-string", testingServer.getConnectString());
|
||||
System.setProperty("metadata.store.zookeeper.encoding", StandardCharsets.US_ASCII.name());
|
||||
|
||||
return testingServer;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(DynamoDbMetadataStore.class)
|
||||
protected static class DynamoDbMockConfig {
|
||||
|
||||
@Bean
|
||||
public static AmazonDynamoDBAsync dynamoDB() {
|
||||
AmazonDynamoDBAsync dynamoDb = mock(AmazonDynamoDBAsync.class);
|
||||
willReturn(new DescribeTableResult())
|
||||
.given(dynamoDb)
|
||||
.describeTable(any(DescribeTableRequest.class));
|
||||
|
||||
return dynamoDb;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static AWSCredentialsProvider awsCredentialsProvider() {
|
||||
return mock(AWSCredentialsProvider.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static RegionProvider regionProvider() {
|
||||
return mock(RegionProvider.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>stream-apps-micrometer-common</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-influx</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.pivotal.cfenv</groupId>
|
||||
<artifactId>java-cfenv-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>2.1.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
/**
|
||||
* Micrometer common tags for Cloud Foundry deployment properties. Based on the CF application environment variables:
|
||||
* https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html
|
||||
*
|
||||
* Tags are set only if the "cloud" Spring profile is set. The "cloud" profile is activated automatically when an
|
||||
* application is deployed in CF: https://docs.cloudfoundry.org/buildpacks/java/configuring-service-connections/spring-service-bindings.html#cloud-profiles
|
||||
*
|
||||
* Use the spring.cloud.stream.app.metrics.cf.tags.enabled=false property to disable inserting those tags.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("cloud")
|
||||
@ConditionalOnProperty(name = "spring.cloud.stream.app.metrics.cf.tags.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class CloudFoundryMicrometerCommonTags {
|
||||
|
||||
@Value("${vcap.application.org_name:default}")
|
||||
private String organizationName;
|
||||
|
||||
@Value("${vcap.application.space_id:unknown}")
|
||||
private String spaceId;
|
||||
|
||||
@Value("${vcap.application.space_name:unknown}")
|
||||
private String spaceName;
|
||||
|
||||
@Value("${vcap.application.application_name:unknown}")
|
||||
private String applicationName;
|
||||
|
||||
@Value("${vcap.application.application_id:unknown}")
|
||||
private String applicationId;
|
||||
|
||||
@Value("${vcap.application.application_version:unknown}")
|
||||
private String applicationVersion;
|
||||
|
||||
@Value("${vcap.application.instance_index:0}")
|
||||
private String instanceIndex;
|
||||
|
||||
@Bean
|
||||
public MeterRegistryCustomizer<MeterRegistry> cloudFoundryMetricsCommonTags() {
|
||||
return registry -> registry.config()
|
||||
.commonTags("cf.org.name", organizationName)
|
||||
.commonTags("cf.space.id", spaceId)
|
||||
.commonTags("cf.space.name", spaceName)
|
||||
.commonTags("cf.app.id", applicationId)
|
||||
.commonTags("cf.app.name", applicationName)
|
||||
.commonTags("cf.app.version", applicationVersion)
|
||||
.commonTags("cf.instance.index", instanceIndex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.config.MeterFilter;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Auto configuration extends the micrometer metrics with additional tags such as: stream name, application name,
|
||||
* instance index and guids. Later are necessary to allow discrimination and aggregation of app metrics by external
|
||||
* metrics collection and visualizaiton tools.
|
||||
*
|
||||
* Use the spring.cloud.stream.app.metrics.common.tags.enabled=false property to disable inserting those tags.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "spring.cloud.stream.app.metrics.common.tags.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class SpringCloudStreamMicrometerCommonTags {
|
||||
|
||||
@Value("${spring.cloud.dataflow.stream.name:unknown}")
|
||||
private String streamName;
|
||||
|
||||
@Value("${spring.cloud.dataflow.stream.app.label:unknown}")
|
||||
private String applicationName;
|
||||
|
||||
@Value("${spring.cloud.stream.instanceIndex:0}")
|
||||
private String instanceIndex;
|
||||
|
||||
@Value("${spring.cloud.application.guid:unknown}")
|
||||
private String applicationGuid;
|
||||
|
||||
@Value("${spring.cloud.dataflow.stream.app.type:unknown}")
|
||||
private String applicationType;
|
||||
|
||||
@Bean
|
||||
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
|
||||
return registry -> registry.config()
|
||||
.commonTags("stream.name", streamName)
|
||||
.commonTags("application.name", applicationName)
|
||||
.commonTags("application.type", applicationType)
|
||||
.commonTags("instance.index", instanceIndex)
|
||||
.commonTags("application.guid", applicationGuid);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MeterRegistryCustomizer<MeterRegistry> renameNameTag() {
|
||||
return registry -> {
|
||||
if (registry.getClass().getCanonicalName().contains("AtlasMeterRegistry")) {
|
||||
registry.config().meterFilter(MeterFilter.renameTag("spring.integration", "name", "aname"));
|
||||
}
|
||||
if (registry.getClass().getCanonicalName().contains("InfluxMeterRegistry")) {
|
||||
registry.config().meterFilter(MeterFilter.replaceTagValues("application.name",
|
||||
tagValue -> ("time".equalsIgnoreCase(tagValue)) ? "atime" : tagValue));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
|
||||
/**
|
||||
* Disables all Micrometer Repositories added as App Starters dependencies by default.
|
||||
* That means disabling Datadog, Influx and Prometheus.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class SpringCloudStreamMicrometerEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
protected static final String PROPERTY_SOURCE_KEY_NAME = SpringCloudStreamMicrometerEnvironmentPostProcessor.class.getName();
|
||||
|
||||
private final static String METRICS_PROPERTY_NAME_TEMPLATE = "management.metrics.export.%s.enabled";
|
||||
|
||||
private final static String[] METRICS_REPOSITORY_NAMES =
|
||||
new String[] { "datadog", "influx", "prometheus", "prometheus.rsocket" };
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
if (!environment.containsProperty("management.endpoints.web.exposure.include")) {
|
||||
properties.setProperty("management.endpoints.web.exposure.include", "prometheus");
|
||||
}
|
||||
|
||||
for (String metricsRepositoryName : METRICS_REPOSITORY_NAMES) {
|
||||
String propertyKey = String.format(METRICS_PROPERTY_NAME_TEMPLATE, metricsRepositoryName);
|
||||
|
||||
// Back off if the property is already set.
|
||||
if (!environment.containsProperty(propertyKey)) {
|
||||
properties.setProperty(propertyKey, "false");
|
||||
}
|
||||
}
|
||||
|
||||
// This post-processor is called multiple times but sets the properties only once.
|
||||
if (!properties.isEmpty()) {
|
||||
PropertiesPropertySource propertiesPropertySource =
|
||||
new PropertiesPropertySource(PROPERTY_SOURCE_KEY_NAME, properties);
|
||||
environment.getPropertySources().addLast(propertiesPropertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.stream.app.micrometer.common.SpringCloudStreamMicrometerCommonTags,\
|
||||
org.springframework.cloud.stream.app.micrometer.common.CloudFoundryMicrometerCommonTags
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
org.springframework.cloud.stream.app.micrometer.common.SpringCloudStreamMicrometerEnvironmentPostProcessor
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.micrometer.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
import io.micrometer.core.instrument.simple.SimpleConfig;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import io.pivotal.cfenv.test.CfEnvTestUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimplePropertiesConfigAdapter;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = AbstractMicrometerTagTest.AutoConfigurationApplication.class)
|
||||
public class AbstractMicrometerTagTest {
|
||||
|
||||
@Autowired
|
||||
protected SimpleMeterRegistry simpleMeterRegistry;
|
||||
|
||||
@Autowired
|
||||
protected ConfigurableApplicationContext context;
|
||||
|
||||
protected Meter meter;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
assertNotNull(simpleMeterRegistry);
|
||||
meter = simpleMeterRegistry.find("jvm.memory.committed").meter();
|
||||
assertNotNull("The jvm.memory.committed meter mast be present in SpringBoot apps!", meter);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws IOException {
|
||||
String serviceJson = StreamUtils.copyToString(new DefaultResourceLoader().getResource(
|
||||
"classpath:/org/springframework/cloud/stream/app/micrometer/common/pcf-scs-info.json")
|
||||
.getInputStream(), Charset.forName("UTF-8"));
|
||||
CfEnvTestUtils.mockVcapServicesFromString(serviceJson);
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableConfigurationProperties(SimpleProperties.class)
|
||||
public static class AutoConfigurationApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AutoConfigurationApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleMeterRegistry simpleMeterRegistry(SimpleConfig config, Clock clock) {
|
||||
return new SimpleMeterRegistry(config, clock);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SimpleConfig simpleConfig(SimpleProperties simpleProperties) {
|
||||
return new SimplePropertiesConfigAdapter(simpleProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.runners.Enclosed;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@RunWith(Enclosed.class)
|
||||
public class CloudFoundryMicrometerCommonTagsTest {
|
||||
|
||||
@ActiveProfiles("cloud")
|
||||
public static class ActiveCloudProfileDefaultValues extends AbstractMicrometerTagTest {
|
||||
@Test
|
||||
public void testDefaultTagValues() {
|
||||
assertThat(meter.getId().getTag("cf.org.name"), is("default"));
|
||||
assertThat(meter.getId().getTag("cf.space.id"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("cf.space.name"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("cf.app.name"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("cf.app.id"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("cf.app.version"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("cf.instance.index"), is("0"));
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"vcap.application.org_name=PivotalOrg",
|
||||
"vcap.application.space_id=SpringSpaceId",
|
||||
"vcap.application.space_name=SpringSpace",
|
||||
"vcap.application.application_name=App666",
|
||||
"vcap.application.application_id=666guid",
|
||||
"vcap.application.application_version=2.0",
|
||||
"vcap.application.instance_index=123" })
|
||||
@ActiveProfiles("cloud")
|
||||
public static class ActiveCloudProfile extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testPresetTagValues() {
|
||||
assertThat(meter.getId().getTag("cf.org.name"), is("PivotalOrg"));
|
||||
assertThat(meter.getId().getTag("cf.space.id"), is("SpringSpaceId"));
|
||||
assertThat(meter.getId().getTag("cf.space.name"), is("SpringSpace"));
|
||||
assertThat(meter.getId().getTag("cf.app.name"), is("App666"));
|
||||
assertThat(meter.getId().getTag("cf.app.id"), is("666guid"));
|
||||
assertThat(meter.getId().getTag("cf.app.version"), is("2.0"));
|
||||
assertThat(meter.getId().getTag("cf.instance.index"), is("123"));
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"vcap.application.org_name=PivotalOrg",
|
||||
"vcap.application.space_id=SpringSpaceId",
|
||||
"vcap.application.space_name=SpringSpace",
|
||||
"vcap.application.application_name=App666",
|
||||
"vcap.application.application_id=666guid",
|
||||
"vcap.application.application_version=2.0",
|
||||
"vcap.application.instance_index=123" })
|
||||
public static class InactiveCloudProfile extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testDisabledTagValues() {
|
||||
assertThat(meter.getId().getTag("cf.org.name"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.space.id"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.space.name"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.app.name"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.app.id"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.app.version"), is(Matchers.nullValue()));
|
||||
assertThat(meter.getId().getTag("cf.instance.index"), is(Matchers.nullValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = { "spring.cloud.stream.app.metrics.cf.tags.enabled=false" })
|
||||
@ActiveProfiles("cloud")
|
||||
public static class ActiveCloudProfileDisabledProperty extends InactiveCloudProfile {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.micrometer.common;
|
||||
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
import io.micrometer.influx.InfluxMeterRegistry;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = InfluxReservedKeywordHandlingTest.AutoConfigurationApplication.class,
|
||||
properties = {
|
||||
"management.metrics.export.influx.enabled=true",
|
||||
"spring.cloud.dataflow.stream.app.label=time" })
|
||||
public class InfluxReservedKeywordHandlingTest {
|
||||
|
||||
@Autowired
|
||||
protected InfluxMeterRegistry influxMeterRegistry;
|
||||
|
||||
@Test
|
||||
public void testPresetTagValues() {
|
||||
assertNotNull(influxMeterRegistry);
|
||||
Meter meter = influxMeterRegistry.find("jvm.memory.committed").meter();
|
||||
assertNotNull("The jvm.memory.committed meter mast be present in SpringBoot apps!", meter);
|
||||
|
||||
assertThat(meter.getId().getTag("application.name"), is("atime"));
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
public static class AutoConfigurationApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AutoConfigurationApplication.class, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.runners.Enclosed;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@RunWith(Enclosed.class)
|
||||
public class SpringCloudStreamMicrometerCommonTagsTest {
|
||||
|
||||
public static class TestDefaultTagValues extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testDefaultTagValues() {
|
||||
assertThat(meter.getId().getTag("stream.name"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("application.name"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("instance.index"), is("0"));
|
||||
assertThat(meter.getId().getTag("application.type"), is("unknown"));
|
||||
assertThat(meter.getId().getTag("application.guid"), is("unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"spring.cloud.dataflow.stream.name=myStream",
|
||||
"spring.cloud.dataflow.stream.app.label=myApp",
|
||||
"spring.cloud.stream.instanceIndex=666",
|
||||
"spring.cloud.application.guid=666guid",
|
||||
"spring.cloud.dataflow.stream.app.type=source" })
|
||||
public static class TestPresetTagValues extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testPresetTagValues() {
|
||||
assertThat(meter.getId().getTag("stream.name"), is("myStream"));
|
||||
assertThat(meter.getId().getTag("application.name"), is("myApp"));
|
||||
assertThat(meter.getId().getTag("instance.index"), is("666"));
|
||||
assertThat(meter.getId().getTag("application.type"), is("source"));
|
||||
assertThat(meter.getId().getTag("application.guid"), is("666guid"));
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = { "spring.cloud.stream.app.metrics.common.tags.enabled=false" })
|
||||
public static class TestDisabledTagValues extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testDefaultTagValues() {
|
||||
assertThat(meter.getId().getTag("stream.name"), is(nullValue()));
|
||||
assertThat(meter.getId().getTag("application.name"), is(nullValue()));
|
||||
assertThat(meter.getId().getTag("instance.index"), is(nullValue()));
|
||||
assertThat(meter.getId().getTag("application.type"), is(nullValue()));
|
||||
assertThat(meter.getId().getTag("application.guid"), is(nullValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.micrometer.common;
|
||||
|
||||
import org.hamcrest.core.Is;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.runners.Enclosed;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@RunWith(Enclosed.class)
|
||||
public class SpringCloudStreamMicrometerEnvironmentPostProcessorTest {
|
||||
|
||||
public static class TestDefaultMetricsEnabledProperties extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testDefaultProperties() {
|
||||
assertNotNull(context);
|
||||
|
||||
PropertySource propertySource = context.getEnvironment().getPropertySources()
|
||||
.get(SpringCloudStreamMicrometerEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNotNull("Property source "
|
||||
+ SpringCloudStreamMicrometerEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is null",
|
||||
propertySource);
|
||||
|
||||
assertThat(propertySource.getProperty("management.metrics.export.influx.enabled"), Is.is("false"));
|
||||
assertThat(propertySource.getProperty("management.metrics.export.prometheus.enabled"), Is.is("false"));
|
||||
assertThat(propertySource.getProperty("management.metrics.export.prometheus.rsocket.enabled"), Is.is("false"));
|
||||
assertThat(propertySource.getProperty("management.metrics.export.datadog.enabled"), Is.is("false"));
|
||||
|
||||
assertThat(propertySource.getProperty("management.endpoints.web.exposure.include"), Is.is("prometheus"));
|
||||
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.influx.enabled"), Is.is("false"));
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.prometheus.enabled"), Is.is("false"));
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.datadog.enabled"), Is.is("false"));
|
||||
|
||||
assertThat(context.getEnvironment().getProperty("management.endpoints.web.exposure.include"), Is.is("prometheus"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"management.metrics.export.simple.enabled=true",
|
||||
"management.metrics.export.influx.enabled=true",
|
||||
"management.metrics.export.prometheus.enabled=true",
|
||||
"management.metrics.export.prometheus.rsocket.enabled=true",
|
||||
"management.metrics.export.datadog.enabled=true",
|
||||
"management.endpoints.web.exposure.include=info,health"})
|
||||
public static class TestOverrideMetricsEnabledProperties extends AbstractMicrometerTagTest {
|
||||
|
||||
@Test
|
||||
public void testOverrideProperties() {
|
||||
assertNotNull(context);
|
||||
|
||||
PropertySource propertySource = context.getEnvironment().getPropertySources()
|
||||
.get(SpringCloudStreamMicrometerEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNull("Property source "
|
||||
+ SpringCloudStreamMicrometerEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is not null",
|
||||
propertySource);
|
||||
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.influx.enabled"), Is.is("true"));
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.prometheus.enabled"), Is.is("true"));
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.prometheus.rsocket.enabled"), Is.is("true"));
|
||||
assertThat(context.getEnvironment().getProperty("management.metrics.export.datadog.enabled"), Is.is("true"));
|
||||
|
||||
assertThat(context.getEnvironment().getProperty("management.endpoints.web.exposure.include"), Is.is("info,health"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"sso":[{
|
||||
"name": "sso",
|
||||
"label": "sso",
|
||||
"plan": "notfree",
|
||||
"tags": ["configuration"],
|
||||
"credentials":{
|
||||
"uri": "https://pivotal.io",
|
||||
"client_id": "fakeClientId",
|
||||
"client_secret": "fakeSecret",
|
||||
"access_token_uri": "token"
|
||||
}
|
||||
}]}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-postprocessor-common</artifactId>
|
||||
<name>stream-apps-postprocessor-common</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.postprocessor;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* An {@link EnvironmentPostProcessor} to set the {@code spring.cloud.stream.bindings.{input,output}.contentType}
|
||||
* channel properties to a default of {@code application/octet-stream} if it has not been set already.
|
||||
*
|
||||
* Subclasses may extend this class to change the default content type and channel name(s).
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
*/
|
||||
public class ContentTypeEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
private Map<String, String> channelMap = createChannelMap();
|
||||
|
||||
private Map<String, String> createChannelMap() {
|
||||
Map<String, String> channelMap = new HashMap<>();
|
||||
channelMap.put(Sink.INPUT, "application/octet-stream");
|
||||
channelMap.put(Source.OUTPUT, "application/octet-stream");
|
||||
|
||||
return channelMap;
|
||||
}
|
||||
|
||||
protected static final String PROPERTY_SOURCE_KEY_NAME = ContentTypeEnvironmentPostProcessor.class.getName();
|
||||
protected static final String CONTENT_TYPE_PROPERTY_PREFIX = "spring.cloud.stream.bindings.";
|
||||
protected static final String CONTENT_TYPE_PROPERTY_SUFFIX = ".contentType";
|
||||
|
||||
public ContentTypeEnvironmentPostProcessor() {
|
||||
super();
|
||||
}
|
||||
|
||||
protected ContentTypeEnvironmentPostProcessor(Map<String, String> channelMap) {
|
||||
this.channelMap = channelMap;
|
||||
}
|
||||
|
||||
protected ContentTypeEnvironmentPostProcessor(String contentType) {
|
||||
for (Map.Entry<String, String> channel : channelMap.entrySet()) {
|
||||
channel.setValue(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
protected ContentTypeEnvironmentPostProcessor(String channelName, String contentType) {
|
||||
channelMap.put(channelName, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
for (Map.Entry<String, String> channel : channelMap.entrySet()) {
|
||||
String propertyKey = CONTENT_TYPE_PROPERTY_PREFIX + channel.getKey() + CONTENT_TYPE_PROPERTY_SUFFIX;
|
||||
|
||||
if (!configurableEnvironment.containsProperty(propertyKey)) {
|
||||
properties.setProperty(propertyKey, channel.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
PropertiesPropertySource propertiesPropertySource =
|
||||
new PropertiesPropertySource(PROPERTY_SOURCE_KEY_NAME, properties);
|
||||
configurableEnvironment.getPropertySources().addLast(propertiesPropertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.postprocessor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.cloud.stream.messaging.Sink;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test cases for {@ContentTypeEnvironmentPostProcessor}.
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
*/
|
||||
public class ContentTypeEnvironmentPostProcessorTests {
|
||||
@Test
|
||||
public void testPostProcessorDefaults() {
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment();
|
||||
|
||||
PropertySource propertySource = configurableEnvironment.getPropertySources()
|
||||
.get(ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNotNull("Property source " + ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is null",
|
||||
propertySource);
|
||||
|
||||
assertTrue("Unexpected input content type", propertySource.getProperty(getContentTypeProperty(Sink.INPUT))
|
||||
.equals("application/octet-stream"));
|
||||
|
||||
assertTrue("Unexpected output content type", propertySource.getProperty(getContentTypeProperty(Source.OUTPUT))
|
||||
.equals("application/octet-stream"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserDefinedOutputContentType() {
|
||||
PropertiesPropertySource testProperties = buildTestProperties(Source.OUTPUT, "text/plain");
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment(testProperties);
|
||||
|
||||
assertTrue("Output contentType property key not found",
|
||||
configurableEnvironment.containsProperty(getContentTypeProperty(Source.OUTPUT)));
|
||||
|
||||
assertTrue("Unexpected output content type", configurableEnvironment.getProperty(getContentTypeProperty(Source.OUTPUT))
|
||||
.equals("text/plain"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserDefinedInputContentType() {
|
||||
PropertiesPropertySource testProperties = buildTestProperties(Sink.INPUT, "text/html");
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment(testProperties);
|
||||
|
||||
assertTrue("Input contentType property key not found",
|
||||
configurableEnvironment.containsProperty(getContentTypeProperty(Sink.INPUT)));
|
||||
|
||||
assertTrue("Unexpected input content type", configurableEnvironment.getProperty(getContentTypeProperty(Sink.INPUT))
|
||||
.equals("text/html"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigureCustomChannel() {
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment(new CustomChannel());
|
||||
|
||||
PropertySource propertySource = configurableEnvironment.getPropertySources()
|
||||
.get(ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNotNull("Property source " + ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is null",
|
||||
propertySource);
|
||||
|
||||
assertTrue("myChannelName contentType property key not found",
|
||||
propertySource.containsProperty(getContentTypeProperty("myChannelName")));
|
||||
|
||||
assertTrue("Unexpected myChannelName content type", propertySource.getProperty(getContentTypeProperty("myChannelName"))
|
||||
.equals("application/octet-stream"));
|
||||
}
|
||||
|
||||
public static class CustomChannel extends ContentTypeEnvironmentPostProcessor {
|
||||
private static final String CHANNEL_NAME = "myChannelName";
|
||||
private static final String CONTENT_TYPE = "application/octet-stream";
|
||||
|
||||
public CustomChannel() {
|
||||
super(CHANNEL_NAME, CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigureDefaultChannelsCustomContentType() {
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment(new DefaultChannelsCustomContentTypes());
|
||||
|
||||
PropertySource propertySource = configurableEnvironment.getPropertySources()
|
||||
.get(ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNotNull("Property source " + ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is null",
|
||||
propertySource);
|
||||
|
||||
assertTrue("Output contentType property key not found",
|
||||
propertySource.containsProperty(getContentTypeProperty(Source.OUTPUT)));
|
||||
|
||||
assertTrue("Unexpected output content type", propertySource.getProperty(getContentTypeProperty(Source.OUTPUT))
|
||||
.equals("image/jpeg"));
|
||||
|
||||
assertTrue("Input contentType property key not found",
|
||||
propertySource.containsProperty(getContentTypeProperty(Sink.INPUT)));
|
||||
|
||||
assertTrue("Unexpected input content type", propertySource.getProperty(getContentTypeProperty(Sink.INPUT))
|
||||
.equals("image/gif"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigureDefaultChannelsSameContentType() {
|
||||
ConfigurableEnvironment configurableEnvironment = getEnvironment(new ChannelSameContentType());
|
||||
|
||||
PropertySource propertySource = configurableEnvironment.getPropertySources()
|
||||
.get(ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME);
|
||||
|
||||
assertNotNull("Property source " + ContentTypeEnvironmentPostProcessor.PROPERTY_SOURCE_KEY_NAME + " is null",
|
||||
propertySource);
|
||||
|
||||
assertTrue("Output contentType property key not found",
|
||||
propertySource.containsProperty(getContentTypeProperty(Source.OUTPUT)));
|
||||
|
||||
assertTrue("Unexpected output content type", propertySource.getProperty(getContentTypeProperty(Source.OUTPUT))
|
||||
.equals("image/jpeg"));
|
||||
|
||||
assertTrue("Input contentType property key not found",
|
||||
propertySource.containsProperty(getContentTypeProperty(Sink.INPUT)));
|
||||
|
||||
assertTrue("Unexpected input content type", propertySource.getProperty(getContentTypeProperty(Sink.INPUT))
|
||||
.equals("image/jpeg"));
|
||||
}
|
||||
|
||||
public static class ChannelSameContentType extends ContentTypeEnvironmentPostProcessor {
|
||||
private static final String CONTENT_TYPE = "image/jpeg";
|
||||
|
||||
public ChannelSameContentType() {
|
||||
super(CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultChannelsCustomContentTypes extends ContentTypeEnvironmentPostProcessor {
|
||||
private static Map<String, String> CHANNEL_MAP = createChannelMap();
|
||||
|
||||
private static Map<String, String> createChannelMap() {
|
||||
Map<String, String> channelMap = new HashMap<>();
|
||||
channelMap.put(Source.OUTPUT, "image/jpeg");
|
||||
channelMap.put(Sink.INPUT, "image/gif");
|
||||
|
||||
return channelMap;
|
||||
}
|
||||
|
||||
public DefaultChannelsCustomContentTypes() {
|
||||
super(CHANNEL_MAP);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getContentTypeProperty(String channelName) {
|
||||
return ContentTypeEnvironmentPostProcessor.CONTENT_TYPE_PROPERTY_PREFIX + channelName
|
||||
+ ContentTypeEnvironmentPostProcessor.CONTENT_TYPE_PROPERTY_SUFFIX;
|
||||
}
|
||||
|
||||
private PropertiesPropertySource buildTestProperties(String channelName, String contentType) {
|
||||
Properties testProperties = new Properties();
|
||||
testProperties.setProperty(getContentTypeProperty(channelName), contentType);
|
||||
|
||||
return new PropertiesPropertySource("test-properties", testProperties);
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment getEnvironment() {
|
||||
return getEnvironment(null, null);
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment getEnvironment(PropertiesPropertySource propertiesPropertySource) {
|
||||
return getEnvironment(propertiesPropertySource, null);
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment getEnvironment(EnvironmentPostProcessor environmentPostProcessor) {
|
||||
return getEnvironment(null, environmentPostProcessor);
|
||||
}
|
||||
|
||||
private ConfigurableEnvironment getEnvironment(PropertiesPropertySource propertiesPropertySource,
|
||||
EnvironmentPostProcessor environmentPostProcessor) {
|
||||
SpringApplication springApplication = new SpringApplicationBuilder()
|
||||
.sources(ContentTypeEnvironmentPostProcessorTests.class)
|
||||
.web(WebApplicationType.NONE).build();
|
||||
|
||||
ConfigurableApplicationContext context = springApplication.run();
|
||||
|
||||
if (propertiesPropertySource != null) {
|
||||
context.getEnvironment().getPropertySources().addFirst(propertiesPropertySource);
|
||||
}
|
||||
|
||||
if (environmentPostProcessor == null) {
|
||||
environmentPostProcessor = new ContentTypeEnvironmentPostProcessor();
|
||||
}
|
||||
|
||||
environmentPostProcessor.postProcessEnvironment(context.getEnvironment(), springApplication);
|
||||
|
||||
ConfigurableEnvironment configurableEnvironment = context.getEnvironment();
|
||||
context.close();
|
||||
|
||||
return configurableEnvironment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
=== `App Starters Security` Common Module
|
||||
|
||||
Spring Boot auto-configuration to manage the web security of the application starters.
|
||||
|
||||
When the `app-starters-security-common` dependency is on the classpath, the `spring.cloud.streamapp.security.enabled` and `spring.cloud.streamapp.security.csrf-enabled` properties control the application security behavior.
|
||||
|
||||
By default the security is enabled allowing unauthorized access only to the `Info` and `Health` endpoints.
|
||||
|
||||
The `spring.cloud.streamapp.security.enabled = false` completely surpass the application security.
|
||||
|
||||
For secured application setting `spring.cloud.streamapp.security.csrf-enabled = false` disables security for the CSRF access.
|
||||
|
||||
With security enabled (`spring.cloud.streamapp.security.enabled = true`) and `actuator` dependency on the classpath, the `(Reactive)ManagementWebSecurityAutoConfiguration` is activated, providing unauthenticated access to the `HealthEndpoint` and `InfoEndpoint`.
|
||||
|
||||
If the user specifies their own `WebSecurityConfigurerAdapter` (for MVC application), this configuration will back-off completely and the user should specify all the bits that they want to configure as part of the custom security configuration.
|
||||
For reactive (WebFlux) application the same effect can be achieved with a custom `WebFilterChainProxy` bean.
|
||||
|
||||
=== Configuration
|
||||
To include app starters security management for a stream app, just include a dependency on this module.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>app-starters-security-common</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
|
||||
All Spring Cloud Stream app starters that inherit form the `core` pom have the `app-starters-security-common` dependency included by default.
|
||||
|
||||
* `spring.cloud.streamapp.security.enabled` (default: `true`). If set to `false` it surpasses the boot security.
|
||||
* `spring.cloud.streamapp.security.csrf-enabled` (default: `true`). If set to `false`, for secured applications it enables CQRS.
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-security-common</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<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-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@Conditional(OnHttpCsrfOrSecurityDisabled.class)
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Flux.class, EnableWebFluxSecurity.class, WebFilterChainProxy.class, WebFluxConfigurer.class })
|
||||
@ConditionalOnMissingBean(WebFilterChainProxy.class)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@AutoConfigureBefore(value = { ReactiveManagementWebSecurityAutoConfiguration.class,
|
||||
ReactiveSecurityAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(AppStarterWebSecurityAutoConfigurationProperties.class)
|
||||
public class AppStarterWebFluxSecurityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
|
||||
AppStarterWebSecurityAutoConfigurationProperties securityProperties) {
|
||||
if (!securityProperties.isCsrfEnabled()) {
|
||||
http.csrf().disable();
|
||||
}
|
||||
if (!securityProperties.isEnabled()) {
|
||||
http.authorizeExchange()
|
||||
.anyExchange()
|
||||
.permitAll();
|
||||
}
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.security.common;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
@Conditional(OnHttpCsrfOrSecurityDisabled.class)
|
||||
@Configuration
|
||||
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@AutoConfigureBefore(value = { ManagementWebSecurityAutoConfiguration.class, SecurityAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(AppStarterWebSecurityAutoConfigurationProperties.class)
|
||||
@EnableWebSecurity
|
||||
public class AppStarterWebSecurityAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
WebSecurityConfigurerAdapter appStarterWebSecurityConfigurerAdapter(
|
||||
AppStarterWebSecurityAutoConfigurationProperties securityProperties) {
|
||||
|
||||
|
||||
return new WebSecurityConfigurerAdapter() {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
super.configure(http);
|
||||
if (!securityProperties.isCsrfEnabled()) {
|
||||
http.csrf().disable();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity builder) {
|
||||
if (!securityProperties.isEnabled()) {
|
||||
builder.ignoring().antMatchers("/**");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.security.common;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@code AppStarterWebSecurityAutoConfiguration} properties.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.streamapp.security")
|
||||
public class AppStarterWebSecurityAutoConfigurationProperties {
|
||||
|
||||
|
||||
/**
|
||||
* The security enabling flag.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* The security CSRF enabling flag. Makes sense only if security 'enabled` is `true'.
|
||||
*/
|
||||
private boolean csrfEnabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isCsrfEnabled() {
|
||||
return this.csrfEnabled;
|
||||
}
|
||||
|
||||
public void setCsrfEnabled(boolean csrfEnabled) {
|
||||
this.csrfEnabled = csrfEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
/**
|
||||
* An {@link AnyNestedCondition} to enable app starters-specific security auto-configuration
|
||||
* overriding out-of-the-box one in Spring Boot.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class OnHttpCsrfOrSecurityDisabled extends AnyNestedCondition {
|
||||
|
||||
OnHttpCsrfOrSecurityDisabled() {
|
||||
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = "spring.cloud.streamapp.security.enabled", havingValue = "false")
|
||||
static class SecurityDisabled {
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(name = "spring.cloud.streamapp.security.csrf-enabled", havingValue = "false")
|
||||
static class HttpCsrfDisabled {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.stream.app.security.common.AppStarterWebSecurityAutoConfiguration,\
|
||||
org.springframework.cloud.stream.app.security.common.AppStarterWebFluxSecurityAutoConfiguration
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.security.common;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
public abstract class AbstractSecurityCommonTests {
|
||||
|
||||
@Autowired
|
||||
protected TestRestTemplate restTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
public static class AutoConfigurationApplication {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=reactive",
|
||||
"spring.cloud.streamapp.security.enabled=false",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class ReactiveSecurityDisabledManagementSecurityEnabledTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=reactive",
|
||||
"org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration"
|
||||
+ ",org.springframework.cloud.stream.app.security.common.AppStarterWebFluxSecurityAutoConfiguration",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class ReactiveSecurityEnabledManagementSecurityDisabledAuthorizedAccessTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
restTemplate.getRestTemplate().getInterceptors().add(new BasicAuthenticationInterceptor(
|
||||
securityProperties.getUser().getName(), securityProperties.getUser().getPassword()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=reactive",
|
||||
"spring.autoconfigure.exclude=" +
|
||||
"org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration"
|
||||
+ ",org.springframework.cloud.stream.app.security.common.AppStarterWebFluxSecurityAutoConfiguration",
|
||||
"management.endpoints.web.exposure.include=health,info" })
|
||||
public class ReactiveSecurityEnabledManagementSecurityDisabledUnauthorizedAccessTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=reactive",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class ReactiveSecurityEnabledManagementSecurityEnabledTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
// The ManagementWebSecurityAutoConfiguration exposes only Info and Health endpoint not Env!
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=servlet",
|
||||
"spring.cloud.streamapp.security.enabled=false",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class SecurityDisabledManagementSecurityEnabledTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=servlet",
|
||||
"spring.autoconfigure.exclude=org.springframework.boot.actuate.autoconfigure.security.servlet" +
|
||||
".ManagementWebSecurityAutoConfiguration"
|
||||
+ ",org.springframework.cloud.stream.app.security.common.AppStarterWebSecurityAutoConfiguration",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class SecurityEnabledManagementSecurityDisabledAuthorizedAccessTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
restTemplate.getRestTemplate().getInterceptors().add(new BasicAuthenticationInterceptor(
|
||||
securityProperties.getUser().getName(), securityProperties.getUser().getPassword()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=servlet",
|
||||
"spring.autoconfigure.exclude=" +
|
||||
"org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration"
|
||||
+ ",org.springframework.cloud.stream.app.security.common.AppStarterWebSecurityAutoConfiguration",
|
||||
"management.endpoints.web.exposure.include=health,info" })
|
||||
public class SecurityEnabledManagementSecurityDisabledUnauthorizedAccessTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("Unauthorized", health.get("error"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("Unauthorized", info.get("error"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.security.common;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
@TestPropertySource(properties = {
|
||||
"spring.main.web-application-type=servlet",
|
||||
"management.endpoints.web.exposure.include=health,info,env",
|
||||
"info.name=MY TEST APP" })
|
||||
public class SecurityEnabledManagementSecurityEnabledTests extends AbstractSecurityCommonTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testHealthEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/health", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map health = response.getBody();
|
||||
assertEquals("UP", health.get("status"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testInfoEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/info", Map.class);
|
||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
Map info = response.getBody();
|
||||
assertEquals("MY TEST APP", info.get("name"));
|
||||
}
|
||||
|
||||
// The ManagementWebSecurityAutoConfiguration exposes only Info and Health endpoint not Env!
|
||||
@Test
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void testEnvEndpoint() {
|
||||
ResponseEntity<Map> response = this.restTemplate.getForEntity("/actuator/env", Map.class);
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
|
||||
assertTrue(response.hasBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
=== `Data Flow Task Launch Request Support` Common Module
|
||||
|
||||
This artifact contains a Spring Boot auto-configuration providing components to produce a Task Launch Request payload.
|
||||
In theory any message source can be used to launch a Task. The task launch request is compatible with the
|
||||
task-launcher-dataflow sink.
|
||||
|
||||
|
||||
==== Data Flow Task Launch Request
|
||||
|
||||
Data Flow Task Launch Request requests require a Data Flow server with an existing task definition.
|
||||
The task launch request contains the name of the task to launch. This must the name of a defined task in Data Flow.
|
||||
You may optionally provide command line arguments and deployment properties.
|
||||
|
||||
===== Task Name
|
||||
|
||||
The task name is a required field. This may be statically configured by setting `task.launch.request.task-name`,
|
||||
extracted from the Message by setting `task.launch.request.task-name-expression`.
|
||||
You may also override the default implementation of the `TaskNameMessageMapper` bean to enable more complex runtime task name mappings.
|
||||
|
||||
|
||||
===== Task Command Line Arguments
|
||||
|
||||
The launched task often requires additional data which may be passed as command line arguments.
|
||||
The `task.launch.request.args` property accepts a comma delimited string of key-value pairs, for example
|
||||
`key1=val1,key2=val2`. In addition, a `task.launch.request.arg-expressions` allows you to use SpEL expressions to evaluate
|
||||
message contents to provide command line arguments.
|
||||
For example, `task.launch.request.arg-expressions=foo=payload.toUpperCase(),bar=payload.substring(0,2)`.
|
||||
|
||||
You may also provide override implementation of the `CommandLineArgumentsMessageMapper` bean to implement more complex logic.
|
||||
|
||||
===== Task Deployment Properties
|
||||
|
||||
Deployment properties are platform-specific configuration used by the `TaskLauncher` and are always statically configured by
|
||||
setting `task.launch.request.deployment-properties` and apply to every task launch request.
|
||||
|
||||
=== Configuration
|
||||
To enable any stream app to transform its output to a Task Launch Request, include a dependency on this module
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>app-starters-task-launch-request-common</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Setting the application property `spring.cloud.stream.function.definition=taskLaunchRequest` is required to execute the transformation.
|
||||
You may safely add the above dependency to applications which optionally produce a task launch request.
|
||||
|
||||
|
||||
`TaskLaunchRequestIntegrationTests` provides some configuration examples.
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-task-launch-request-common</artifactId>
|
||||
<name>stream-apps-task-launch-request-common</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-file-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<classifier>test-binder</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DataFlowTaskLaunchRequest {
|
||||
@JsonProperty("args")
|
||||
private List<String> commandlineArguments = new ArrayList<>();
|
||||
|
||||
@JsonProperty("deploymentProps")
|
||||
private Map<String, String> deploymentProperties = new HashMap<>();
|
||||
|
||||
@JsonProperty("name")
|
||||
private String taskName;
|
||||
|
||||
public void setCommandlineArguments(List<String> commandlineArguments) {
|
||||
this.commandlineArguments = new ArrayList<>(commandlineArguments);
|
||||
}
|
||||
|
||||
public List<String> getCommandlineArguments() {
|
||||
return this.commandlineArguments;
|
||||
}
|
||||
|
||||
public void setDeploymentProperties(Map<String, String> deploymentProperties) {
|
||||
this.deploymentProperties = deploymentProperties;
|
||||
}
|
||||
|
||||
public Map<String, String> getDeploymentProperties() {
|
||||
return this.deploymentProperties;
|
||||
}
|
||||
|
||||
public void setTaskName(String taskName) {
|
||||
this.taskName = taskName;
|
||||
}
|
||||
|
||||
public String getTaskName() {
|
||||
return this.taskName;
|
||||
}
|
||||
|
||||
public DataFlowTaskLaunchRequest addCommmandLineArguments(Collection<String> args) {
|
||||
this.commandlineArguments.addAll(args);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.CommandLineArgumentsMessageMapper;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskLaunchRequestSupplier;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskNameMessageMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
**/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(DataflowTaskLaunchRequestProperties.class)
|
||||
public class DataFlowTaskLaunchRequestAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
public final static String TASK_LAUNCH_REQUEST_FUNCTION_NAME = "taskLaunchRequest";
|
||||
|
||||
/**
|
||||
* A {@link java.util.function.Function} to transform a {@link Message} payload to a {@link DataFlowTaskLaunchRequest}.
|
||||
*
|
||||
* @param taskLaunchRequestMessageProcessor a {@link TaskLaunchRequestMessageProcessor}.
|
||||
*
|
||||
* @return a {code DataFlowTaskLaunchRequest} Message.
|
||||
*/
|
||||
@Bean(name = TASK_LAUNCH_REQUEST_FUNCTION_NAME)
|
||||
@ConditionalOnMissingBean(TaskLaunchRequestFunction.class)
|
||||
public TaskLaunchRequestFunction taskLaunchRequest(TaskLaunchRequestMessageProcessor taskLaunchRequestMessageProcessor) {
|
||||
return message -> taskLaunchRequestMessageProcessor.postProcessMessage(message);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TaskNameMessageMapper.class)
|
||||
public TaskNameMessageMapper taskNameMessageMapper(DataflowTaskLaunchRequestProperties taskLaunchRequestProperties) {
|
||||
if (StringUtils.hasText(taskLaunchRequestProperties.getTaskNameExpression())) {
|
||||
SpelExpressionParser expressionParser = new SpelExpressionParser();
|
||||
Expression taskNameExpression = expressionParser.parseExpression(taskLaunchRequestProperties.getTaskNameExpression());
|
||||
return new ExpressionEvaluatingTaskNameMessageMapper(taskNameExpression, this.evaluationContext);
|
||||
}
|
||||
|
||||
return message -> taskLaunchRequestProperties.getTaskName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CommandLineArgumentsMessageMapper.class)
|
||||
public CommandLineArgumentsMessageMapper commandLineArgumentsMessageMapper(
|
||||
DataflowTaskLaunchRequestProperties dataflowTaskLaunchRequestProperties){
|
||||
|
||||
return new ExpressionEvaluatingCommandLineArgsMapper(dataflowTaskLaunchRequestProperties.getArgExpressions(),
|
||||
this.evaluationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskLaunchRequestSupplier taskLaunchRequestInitializer(
|
||||
DataflowTaskLaunchRequestProperties taskLaunchRequestProperties){
|
||||
return new DataflowTaskLaunchRequestPropertiesInitializer(taskLaunchRequestProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskLaunchRequestMessageProcessor taskLaunchRequestMessageProcessor(
|
||||
TaskLaunchRequestSupplier taskLaunchRequestInitializer,
|
||||
TaskNameMessageMapper taskNameMessageMapper,
|
||||
CommandLineArgumentsMessageMapper commandLineArgumentsMessageMapper) {
|
||||
|
||||
return new TaskLaunchRequestMessageProcessor(taskLaunchRequestInitializer,
|
||||
taskNameMessageMapper,
|
||||
commandLineArgumentsMessageMapper);
|
||||
}
|
||||
|
||||
static class DataflowTaskLaunchRequestPropertiesInitializer extends TaskLaunchRequestSupplier {
|
||||
DataflowTaskLaunchRequestPropertiesInitializer(
|
||||
DataflowTaskLaunchRequestProperties taskLaunchRequestProperties){
|
||||
|
||||
this.commandLineArgumentSupplier(
|
||||
() -> new ArrayList<>(taskLaunchRequestProperties.getArgs())
|
||||
);
|
||||
|
||||
this.deploymentPropertiesSupplier(
|
||||
() -> KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
taskLaunchRequestProperties.getDeploymentProperties())
|
||||
);
|
||||
|
||||
this.taskNameSupplier(()->taskLaunchRequestProperties.getTaskName());
|
||||
}
|
||||
}
|
||||
|
||||
static class ExpressionEvaluatingCommandLineArgsMapper implements CommandLineArgumentsMessageMapper {
|
||||
private final Map<String,Expression> argExpressionsMap;
|
||||
private final EvaluationContext evaluationContext;
|
||||
|
||||
ExpressionEvaluatingCommandLineArgsMapper(String argExpressions, EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
this.argExpressionsMap = new HashMap<>();
|
||||
if (StringUtils.hasText(argExpressions)) {
|
||||
SpelExpressionParser expressionParser = new SpelExpressionParser();
|
||||
|
||||
KeyValueListParser.parseCommaDelimitedKeyValuePairs(argExpressions).forEach(
|
||||
(k,v)-> argExpressionsMap.put(k, expressionParser.parseExpression(v)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> processMessage(Message<?> message) {
|
||||
return evaluateArgExpressions(message);
|
||||
}
|
||||
|
||||
private Collection<String> evaluateArgExpressions(Message<?> message) {
|
||||
List<String> results = new LinkedList<>();
|
||||
this.argExpressionsMap.forEach((k, expression) ->
|
||||
results.add(String.format("%s=%s", k, expression.getValue(this.evaluationContext, message))));
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void createEvaluationContext(){
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.validation.constraints.AssertFalse;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* Base Properties to create a {@link DataFlowTaskLaunchRequest}.
|
||||
*
|
||||
* @author Chris Schaefer
|
||||
* @author David Turanski
|
||||
*/
|
||||
@Validated
|
||||
@ConfigurationProperties("task.launch.request")
|
||||
public class DataflowTaskLaunchRequestProperties {
|
||||
|
||||
/**
|
||||
* Comma separated list of optional args in key=value format.
|
||||
*/
|
||||
private List<String> args = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma separated list of option args as SpEL expressions in key=value format.
|
||||
*/
|
||||
private String argExpressions = "";
|
||||
|
||||
/**
|
||||
* Comma delimited list of deployment properties to be applied to the
|
||||
* TaskLaunchRequest.
|
||||
*/
|
||||
private String deploymentProperties = "";
|
||||
|
||||
/**
|
||||
* The Data Flow task name.
|
||||
*/
|
||||
private String taskName;
|
||||
|
||||
|
||||
/**
|
||||
* A SpEL expression to extract the task name from each Message, using the Message as the evaluation context.
|
||||
*/
|
||||
private String taskNameExpression;
|
||||
|
||||
@NotNull
|
||||
public List<String> getArgs() {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
public void setArgs(List<String> args) {
|
||||
this.args = new ArrayList<>(args);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDeploymentProperties() {
|
||||
return this.deploymentProperties;
|
||||
}
|
||||
|
||||
public void setDeploymentProperties(String deploymentProperties) {
|
||||
this.deploymentProperties = deploymentProperties;
|
||||
}
|
||||
|
||||
public String getTaskName() {
|
||||
return taskName;
|
||||
}
|
||||
|
||||
public void setTaskName(String taskName) {
|
||||
this.taskName = taskName;
|
||||
}
|
||||
|
||||
public String getTaskNameExpression() {
|
||||
return taskNameExpression;
|
||||
}
|
||||
|
||||
public void setTaskNameExpression(String taskNameExpression) {
|
||||
this.taskNameExpression = taskNameExpression;
|
||||
}
|
||||
|
||||
public String getArgExpressions() {
|
||||
return argExpressions;
|
||||
}
|
||||
|
||||
public void setArgExpressions(String argExpressions) {
|
||||
this.argExpressions = argExpressions;
|
||||
}
|
||||
|
||||
@AssertFalse(message = "Cannot specify both 'taskName' and 'taskNameExpression'.")
|
||||
public boolean isTaskNameAndTaskNameExpressionSet() {
|
||||
return StringUtils.hasText(this.taskName) && StringUtils.hasText(this.taskNameExpression);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskNameMessageMapper;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
public class ExpressionEvaluatingTaskNameMessageMapper implements TaskNameMessageMapper {
|
||||
|
||||
private final Expression expression;
|
||||
private final EvaluationContext evaluationContext;
|
||||
|
||||
public ExpressionEvaluatingTaskNameMessageMapper(Expression expression, EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processMessage(Message<?> message) {
|
||||
return expression.getValue(evaluationContext, message).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parses a comma delimited list of key value pairs in which the values can contain commas as well.
|
||||
*
|
||||
* @author Chris Schaeffer
|
||||
* @author David Turanski
|
||||
**/
|
||||
abstract class KeyValueListParser {
|
||||
|
||||
static Map<String, String> parseCommaDelimitedKeyValuePairs(String value) {
|
||||
Map<String, String> keyValuePairs = new HashMap<>();
|
||||
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return keyValuePairs;
|
||||
}
|
||||
|
||||
ArrayList<String> pairs = new ArrayList<>();
|
||||
|
||||
String[] candidates = StringUtils.commaDelimitedListToStringArray(value);
|
||||
|
||||
for (int i = 0; i < candidates.length; i++) {
|
||||
if (i > 0 && !candidates[i].contains("=")) {
|
||||
pairs.add(pairs.get(pairs.size() - 1) + "," + candidates[i]);
|
||||
}
|
||||
else {
|
||||
pairs.add(candidates[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (String pair : pairs) {
|
||||
addKeyValuePair(pair, keyValuePairs);
|
||||
}
|
||||
|
||||
return keyValuePairs;
|
||||
}
|
||||
|
||||
private static void addKeyValuePair(String pair, Map<String, String> properties) {
|
||||
int firstEquals = pair.indexOf('=');
|
||||
if (firstEquals != -1) {
|
||||
properties.put(pair.substring(0, firstEquals).trim(), pair.substring(firstEquals + 1).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* A marker interface useful for unambiguous dependency injection of this Function.
|
||||
*
|
||||
* @author David Turanski
|
||||
**/
|
||||
@FunctionalInterface
|
||||
public interface TaskLaunchRequestFunction extends Function<Message<?>, Message<DataFlowTaskLaunchRequest>> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.CommandLineArgumentsMessageMapper;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskLaunchRequestSupplier;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskNameMessageMapper;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.core.MessagePostProcessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public class TaskLaunchRequestMessageProcessor implements MessagePostProcessor {
|
||||
|
||||
private final TaskNameMessageMapper taskNameMessageMapper;
|
||||
private final CommandLineArgumentsMessageMapper commandLineArgumentsMessageMapper;
|
||||
private final TaskLaunchRequestSupplier taskLaunchRequestInitializer;
|
||||
|
||||
public TaskLaunchRequestMessageProcessor(TaskLaunchRequestSupplier taskLaunchRequestInitializer,
|
||||
TaskNameMessageMapper taskNameMessageMapper,
|
||||
CommandLineArgumentsMessageMapper commandLIneArgumentsMessageMapper) {
|
||||
|
||||
this.taskLaunchRequestInitializer = taskLaunchRequestInitializer;
|
||||
|
||||
this.taskNameMessageMapper = taskNameMessageMapper;
|
||||
|
||||
this.commandLineArgumentsMessageMapper = commandLIneArgumentsMessageMapper;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<DataFlowTaskLaunchRequest> postProcessMessage(Message<?> message) {
|
||||
DataFlowTaskLaunchRequest taskLaunchRequest = taskLaunchRequestInitializer.get();
|
||||
|
||||
if (!StringUtils.hasText(taskLaunchRequest.getTaskName())) {
|
||||
taskLaunchRequest.setTaskName(taskNameMessageMapper.processMessage(message));
|
||||
Assert.hasText(taskLaunchRequest.getTaskName(), ()->
|
||||
"'taskName' is required in " + DataFlowTaskLaunchRequest.class.getName());
|
||||
}
|
||||
|
||||
taskLaunchRequest.addCommmandLineArguments(commandLineArgumentsMessageMapper.processMessage(message));
|
||||
|
||||
MessageBuilder<DataFlowTaskLaunchRequest> builder
|
||||
= MessageBuilder.withPayload(taskLaunchRequest).copyHeaders(message.getHeaders());
|
||||
return adjustHeaders(builder).build();
|
||||
}
|
||||
|
||||
|
||||
private MessageBuilder<DataFlowTaskLaunchRequest> adjustHeaders(MessageBuilder<DataFlowTaskLaunchRequest> builder) {
|
||||
builder.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
|
||||
public interface CommandLineArgumentsMessageMapper extends MessageProcessor<Collection<String>> {
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest.support;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.DataFlowTaskLaunchRequest;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class TaskLaunchRequestSupplier implements Supplier<DataFlowTaskLaunchRequest> {
|
||||
|
||||
private Supplier<String> taskNameSupplier;
|
||||
private Supplier<List<String>> commandLineArgumentsSupplier;
|
||||
private Supplier<Map<String, String>> deploymentPropertiesSupplier;
|
||||
|
||||
|
||||
public TaskLaunchRequestSupplier taskNameSupplier(Supplier<String> taskNameSupplier) {
|
||||
this.taskNameSupplier = taskNameSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskLaunchRequestSupplier commandLineArgumentSupplier(Supplier<List<String>> commandLineArgumentsSupplier) {
|
||||
this.commandLineArgumentsSupplier = commandLineArgumentsSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskLaunchRequestSupplier deploymentPropertiesSupplier(Supplier<Map<String, String>> deploymentPropertiesSupplier) {
|
||||
this.deploymentPropertiesSupplier = deploymentPropertiesSupplier;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlowTaskLaunchRequest get() {
|
||||
|
||||
Assert.notNull(this.taskNameSupplier, "'taskNameSupplier' is required.");
|
||||
|
||||
DataFlowTaskLaunchRequest dataFlowTaskLaunchRequest = new DataFlowTaskLaunchRequest();
|
||||
dataFlowTaskLaunchRequest.setTaskName(this.taskNameSupplier.get());
|
||||
|
||||
if (this.commandLineArgumentsSupplier != null) {
|
||||
dataFlowTaskLaunchRequest.setCommandlineArguments(this.commandLineArgumentsSupplier.get());
|
||||
}
|
||||
|
||||
if (this.deploymentPropertiesSupplier != null) {
|
||||
dataFlowTaskLaunchRequest.setDeploymentProperties(this.deploymentPropertiesSupplier.get());
|
||||
}
|
||||
|
||||
return dataFlowTaskLaunchRequest;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 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.stream.app.tasklaunchrequest.support;
|
||||
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TaskNameMessageMapper extends MessageProcessor<String> {
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
configuration-properties.classes=\
|
||||
org.springframework.cloud.stream.app.tasklaunchrequest.DataflowTaskLaunchRequestProperties
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.stream.app.tasklaunchrequest.DataFlowTaskLaunchRequestAutoConfiguration
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Chris Schaefer
|
||||
* @author David Turanski
|
||||
*/
|
||||
public class KeyValueListParserTests {
|
||||
|
||||
@Test
|
||||
public void testParseSimpleDeploymentProperty() {
|
||||
Map<String, String> deploymentProperties = KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
"app.sftp.param=value");
|
||||
assertTrue("Invalid number of deployment properties: " + deploymentProperties.size(),
|
||||
deploymentProperties.size() == 1);
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.param"));
|
||||
assertEquals("Invalid deployment value", "value", deploymentProperties.get("app.sftp.param"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSimpleDeploymentPropertyMultipleValues() {
|
||||
Map<String, String> deploymentProperties = KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
"app.sftp.param=value1,value2,value3");
|
||||
|
||||
assertTrue("Invalid number of deployment properties: " + deploymentProperties.size(),
|
||||
deploymentProperties.size() == 1);
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.param"));
|
||||
assertEquals("Invalid deployment value", "value1,value2,value3", deploymentProperties.get("app.sftp.param"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseSpelExpressionMultipleValues() {
|
||||
Map<String, String> argExpressions = KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
"arg1=payload.substr(0,2),arg2=headers['foo'],arg3=headers['bar']==false");
|
||||
|
||||
assertTrue("Invalid number of deployment properties: " + argExpressions.size(),
|
||||
argExpressions.size() == 3);
|
||||
assertTrue("Expected deployment key not found", argExpressions.containsKey("arg1"));
|
||||
assertEquals("Invalid deployment value", "payload.substr(0,2)", argExpressions.get("arg1"));
|
||||
|
||||
assertTrue("Expected deployment key not found", argExpressions.containsKey("arg2"));
|
||||
assertEquals("Invalid deployment value", "headers['foo']", argExpressions.get("arg2"));
|
||||
|
||||
assertTrue("Expected deployment key not found", argExpressions.containsKey("arg3"));
|
||||
assertEquals("Invalid deployment value", "headers['bar']==false", argExpressions.get("arg3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMultipleDeploymentPropertiesSingleValue() {
|
||||
Map<String, String> deploymentProperties = KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
"app.sftp.param=value1,app.sftp.other.param=value2");
|
||||
|
||||
assertTrue("Invalid number of deployment properties: " + deploymentProperties.size(),
|
||||
deploymentProperties.size() == 2);
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.param"));
|
||||
assertEquals("Invalid deployment value", "value1", deploymentProperties.get("app.sftp.param"));
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.other.param"));
|
||||
assertEquals("Invalid deployment value", "value2", deploymentProperties.get("app.sftp.other.param"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMultipleDeploymentPropertiesMultipleValues() {
|
||||
DataflowTaskLaunchRequestProperties taskLaunchRequestProperties = new DataflowTaskLaunchRequestProperties();
|
||||
|
||||
Map<String, String> deploymentProperties = KeyValueListParser.parseCommaDelimitedKeyValuePairs(
|
||||
"app.sftp.param=value1,value2,app.sftp.other.param=other1,other2");
|
||||
|
||||
assertTrue("Invalid number of deployment properties: " + deploymentProperties.size(),
|
||||
deploymentProperties.size() == 2);
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.param"));
|
||||
assertEquals("Invalid deployment value", "value1,value2", deploymentProperties.get("app.sftp.param"));
|
||||
assertTrue("Expected deployment key not found", deploymentProperties.containsKey("app.sftp.other.param"));
|
||||
assertEquals("Invalid deployment value", "other1,other2", deploymentProperties.get("app.sftp.other.param"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright 2018 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.stream.app.tasklaunchrequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.CommandLineArgumentsMessageMapper;
|
||||
import org.springframework.cloud.stream.app.tasklaunchrequest.support.TaskNameMessageMapper;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.cloud.stream.messaging.Processor;
|
||||
import org.springframework.cloud.stream.test.binder.MessageCollectorAutoConfiguration;
|
||||
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
**/
|
||||
public class TaskLaunchRequestIntegrationTests {
|
||||
|
||||
private ApplicationContextRunner applicationContextRunner;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
applicationContextRunner =
|
||||
new ApplicationContextRunner().withUserConfiguration(TestChannelBinderConfiguration.class, TestApp.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTaskLaunchRequestPropertiesAreRequired() {
|
||||
|
||||
applicationContextRunner.withPropertyValues("spring.jmx.enabled=false")
|
||||
.run(context -> {
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
Message<byte[]> message =
|
||||
MessageBuilder.withPayload("hello".getBytes()).build();
|
||||
input.send(message);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
assertThat(response.getPayload()).isEqualTo(message.getPayload());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleDataflowTaskLaunchRequest() {
|
||||
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false",
|
||||
"spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name=foo")
|
||||
.run(context -> {
|
||||
DataFlowTaskLaunchRequest dataFlowTaskLaunchRequest = verifyAndreceiveDataFlowTaskLaunchRequest(context);
|
||||
|
||||
assertThat(dataFlowTaskLaunchRequest.getTaskName()).isEqualTo("foo");
|
||||
assertThat(dataFlowTaskLaunchRequest.getCommandlineArguments()).hasSize(0);
|
||||
assertThat(dataFlowTaskLaunchRequest.getDeploymentProperties()).hasSize(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataflowTaskLaunchRequestWithArgsAndDeploymentProperties() {
|
||||
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name=foo", "task.launch.request.args=foo=bar,baz=boo",
|
||||
"task.launch.request.deploymentProperties=count=3")
|
||||
.run(context -> {
|
||||
DataFlowTaskLaunchRequest dataFlowTaskLaunchRequest = verifyAndreceiveDataFlowTaskLaunchRequest(context);
|
||||
|
||||
assertThat(dataFlowTaskLaunchRequest.getTaskName()).isEqualTo("foo");
|
||||
assertThat(dataFlowTaskLaunchRequest.getCommandlineArguments()).containsExactlyInAnyOrder("foo=bar",
|
||||
"baz=boo");
|
||||
assertThat(dataFlowTaskLaunchRequest.getDeploymentProperties()).containsOnly(entry("count", "3"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataflowTaskLaunchRequestWithCommandLineArgsMessageMapper() {
|
||||
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name=foo", "enhanceTLRArgs=true")
|
||||
.run(context -> {
|
||||
|
||||
DataFlowTaskLaunchRequest dataFlowTaskLaunchRequest = verifyAndreceiveDataFlowTaskLaunchRequest(context);
|
||||
|
||||
assertThat(dataFlowTaskLaunchRequest.getTaskName()).isEqualTo("foo");
|
||||
assertThat(dataFlowTaskLaunchRequest.getCommandlineArguments()).hasSize(1);
|
||||
assertThat(dataFlowTaskLaunchRequest.getCommandlineArguments()).containsExactly("runtimeArg");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskLaunchRequestWithArgExpressions() {
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false",
|
||||
"spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name=foo",
|
||||
"task.launch.request.arg-expressions=foo=payload.toUpperCase(),bar=payload.substring(0,2)")
|
||||
.run(context -> {
|
||||
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("hello").build();
|
||||
|
||||
input.send(message);
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
DataFlowTaskLaunchRequest request = objectMapper.readValue(response.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
|
||||
assertThat(request.getCommandlineArguments()).containsExactlyInAnyOrder("foo=HELLO", "bar=he");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskLaunchRequestWithIntPayload() {
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name=foo",
|
||||
"task.launch.request.arg-expressions=i=payload")
|
||||
.run(context -> {
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
Message<Integer> message =
|
||||
MessageBuilder.withPayload(123).build();
|
||||
|
||||
input.send(message);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
DataFlowTaskLaunchRequest request = objectMapper.readValue(response.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
|
||||
assertThat(request.getCommandlineArguments()).containsExactly("i=123");
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void taskNameExpression() {
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"task.launch.request.task-name-expression=payload+'_task'")
|
||||
.run(context -> {
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("foo").build();
|
||||
input.send(message);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
DataFlowTaskLaunchRequest request = objectMapper.readValue(response.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
|
||||
assertThat(request.getTaskName()).isEqualTo("foo_task");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTaskNameExtractor() {
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"customTaskNameExtractor=true")
|
||||
.run(context -> {
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("foo").build();
|
||||
input.send(message);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
DataFlowTaskLaunchRequest request = objectMapper.readValue(response.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
|
||||
assertThat(request.getTaskName()).isEqualTo("fooTask");
|
||||
});
|
||||
//TODO: Workaround for https://github.com/spring-cloud/spring-cloud-stream/issues/1876
|
||||
applicationContextRunner.withPropertyValues(
|
||||
"spring.jmx.enabled=false", "spring.cloud.stream.function.definition=taskLaunchRequest",
|
||||
"customTaskNameExtractor=true")
|
||||
.run(context -> {
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("bar").build();
|
||||
input.send(message);
|
||||
|
||||
Message<byte[]> response = target.receive(1000);
|
||||
assertThat(response).isNotNull();
|
||||
|
||||
DataFlowTaskLaunchRequest request = objectMapper.readValue(response.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
|
||||
assertThat(request.getTaskName()).isEqualTo("defaultTask");
|
||||
});
|
||||
}
|
||||
|
||||
private DataFlowTaskLaunchRequest verifyAndreceiveDataFlowTaskLaunchRequest(ApplicationContext context)
|
||||
throws IOException {
|
||||
MessageChannel input = context.getBean("input", MessageChannel.class);
|
||||
|
||||
OutputDestination target = context.getBean(OutputDestination.class);
|
||||
|
||||
MessageBuilder<byte[]> builder = MessageBuilder.withPayload(new byte[] {});
|
||||
|
||||
ObjectMapper objectMapper = context.getBean(ObjectMapper.class);
|
||||
|
||||
input.send(builder.build());
|
||||
|
||||
Message<byte[]> message = target.receive(1000);
|
||||
|
||||
assertThat(message).isNotNull();
|
||||
|
||||
return objectMapper.readValue(message.getPayload(),
|
||||
DataFlowTaskLaunchRequest.class);
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = { TestSupportBinderAutoConfiguration.class,
|
||||
MessageCollectorAutoConfiguration.class })
|
||||
@EnableBinding(Processor.class)
|
||||
static class TestApp {
|
||||
|
||||
@Bean
|
||||
ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("customTaskNameExtractor")
|
||||
TaskNameMessageMapper taskNameExtractor() {
|
||||
return message -> ((String)(message.getPayload())).equalsIgnoreCase("foo") ?
|
||||
"fooTask" :
|
||||
"defaultTask";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty("enhanceTLRArgs")
|
||||
CommandLineArgumentsMessageMapper commandLineArgumentsProvider(){
|
||||
return message -> Collections.singletonList("runtimeArg");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow flow() {
|
||||
|
||||
return IntegrationFlows.from(Processor.INPUT)
|
||||
.channel(Processor.OUTPUT).get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2018-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.stream.app.tasklaunchrequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.util.TestPropertyValues;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
**/
|
||||
public class TaskLaunchRequestPropertiesTests {
|
||||
|
||||
@Test
|
||||
public void deploymentPropertiesCanBeCustomized() {
|
||||
DataflowTaskLaunchRequestProperties properties = getBatchProperties(
|
||||
"task.launch.request.deploymentProperties:prop1=val1,prop2=val2");
|
||||
assertThat(properties.getDeploymentProperties()).isEqualTo("prop1=val1,prop2=val2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parametersCanBeCustomized() {
|
||||
DataflowTaskLaunchRequestProperties properties = getBatchProperties(
|
||||
"task.launch.request.args:jp1=jpv1,jp2=jpv2");
|
||||
List<String> args = properties.getArgs();
|
||||
|
||||
assertThat(args).isNotNull();
|
||||
assertThat(args).hasSize(2);
|
||||
assertThat(args.get(0)).isEqualTo("jp1=jpv1");
|
||||
assertThat(args.get(1)).isEqualTo("jp2=jpv2");
|
||||
}
|
||||
|
||||
private DataflowTaskLaunchRequestProperties getBatchProperties(String... var) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
if (var != null) {
|
||||
TestPropertyValues.of(var).applyTo(context);
|
||||
}
|
||||
|
||||
context.register(Conf.class);
|
||||
context.refresh();
|
||||
|
||||
return context.getBean(DataflowTaskLaunchRequestProperties.class);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@EnableConfigurationProperties(DataflowTaskLaunchRequestProperties.class)
|
||||
@Import(DataFlowTaskLaunchRequestAutoConfiguration.class)
|
||||
static class Conf {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-common</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-test-support</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-test-support</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 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.stream.app.test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
|
||||
/**
|
||||
* Unlike the {@code PropertiesInitializer}, this does not require boot infrastructure
|
||||
* to add properties to the context. Used for testing generated apps where the
|
||||
* {@code ApplicationContextInitializer} can't be used. Since it's a BDRPP, it runs
|
||||
* before any BFPPs - i.e. as early as possible.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class BinderTestPropertiesInitializer implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
private final ConfigurableApplicationContext context;
|
||||
|
||||
private final Properties properties;
|
||||
|
||||
public BinderTestPropertiesInitializer(ConfigurableApplicationContext context, Properties properties) {
|
||||
this.context = context;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
this.context.getEnvironment().getPropertySources()
|
||||
.addLast(new PropertiesPropertySource("scsAppProperties", properties));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2014-2016 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.stream.app.test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
|
||||
/**
|
||||
* @author David Turanski
|
||||
*/
|
||||
public class PropertiesInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
public static Properties PROPERTIES;
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
|
||||
configurableApplicationContext.getEnvironment().getPropertySources().addLast(new
|
||||
PropertiesPropertySource("applicationOptions", PROPERTIES));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2015-2016 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.stream.app.test.file.remote;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
/**
|
||||
* Abstract base class for tests requiring remote file servers, e.g. (S)FTP.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public abstract class RemoteFileTestSupport {
|
||||
|
||||
protected static final int port = 0;
|
||||
|
||||
@ClassRule
|
||||
public static final TemporaryFolder remoteTemporaryFolder = new TemporaryFolder();
|
||||
|
||||
@ClassRule
|
||||
public static final TemporaryFolder localTemporaryFolder = new TemporaryFolder();
|
||||
|
||||
protected volatile File sourceRemoteDirectory;
|
||||
|
||||
protected volatile File targetRemoteDirectory;
|
||||
|
||||
protected volatile File sourceLocalDirectory;
|
||||
|
||||
protected volatile File targetLocalDirectory;
|
||||
|
||||
public File getSourceRemoteDirectory() {
|
||||
return sourceRemoteDirectory;
|
||||
}
|
||||
|
||||
public File getTargetRemoteDirectory() {
|
||||
return targetRemoteDirectory;
|
||||
}
|
||||
|
||||
public File getSourceLocalDirectory() {
|
||||
return sourceLocalDirectory;
|
||||
}
|
||||
|
||||
public File getTargetLocalDirectory() {
|
||||
return targetLocalDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation creates the following folder structures:
|
||||
*
|
||||
* <pre class="code">
|
||||
* $ tree remoteSource/
|
||||
* remoteSource/
|
||||
* ├── remoteSource1.txt - contains 'source1'
|
||||
* ├── remoteSource2.txt - contains 'source2'
|
||||
* remoteTarget/
|
||||
* $ tree localSource/
|
||||
* localSource/
|
||||
* ├── localSource1.txt - contains 'local1'
|
||||
* ├── localSource2.txt - contains 'local2'
|
||||
* localTarget/
|
||||
* </pre>
|
||||
*
|
||||
* The intent is tests retrieve from remoteSource and verify arrival in localTarget or send from localSource and verify
|
||||
* arrival in remoteTarget.
|
||||
* <p>
|
||||
* Subclasses can change 'remote' in these names by overriding {@link #prefix()} or override this method completely to
|
||||
* create a different structure.
|
||||
* <p>
|
||||
* While a single server exists for all tests, the directory structure is rebuilt for each test.
|
||||
* @throws IOException IO Exception.
|
||||
*/
|
||||
@Before
|
||||
public void setupFolders() throws IOException {
|
||||
String prefix = prefix();
|
||||
recursiveDelete(new File(remoteTemporaryFolder.getRoot(), prefix + "Source"));
|
||||
this.sourceRemoteDirectory = remoteTemporaryFolder.newFolder(prefix + "Source");
|
||||
recursiveDelete(new File(remoteTemporaryFolder.getRoot(), prefix + "Target"));
|
||||
this.targetRemoteDirectory = remoteTemporaryFolder.newFolder(prefix + "Target");
|
||||
recursiveDelete(new File(localTemporaryFolder.getRoot(), "localSource"));
|
||||
this.sourceLocalDirectory = localTemporaryFolder.newFolder("localSource");
|
||||
recursiveDelete(new File(localTemporaryFolder.getRoot(), "localTarget"));
|
||||
this.targetLocalDirectory = localTemporaryFolder.newFolder("localTarget");
|
||||
File file = new File(sourceRemoteDirectory, prefix + "Source1.txt");
|
||||
file.createNewFile();
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write("source1".getBytes());
|
||||
fos.close();
|
||||
file = new File(sourceRemoteDirectory, prefix + "Source2.txt");
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write("source2".getBytes());
|
||||
fos.close();
|
||||
file = new File(sourceLocalDirectory, "localSource1.txt");
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write("local1".getBytes());
|
||||
fos.close();
|
||||
file = new File(sourceLocalDirectory, "localSource2.txt");
|
||||
file.createNewFile();
|
||||
fos = new FileOutputStream(file);
|
||||
fos.write("local2".getBytes());
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public void recursiveDelete(File file) {
|
||||
if (file != null && file.exists()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File fyle : files) {
|
||||
if (fyle.isDirectory()) {
|
||||
recursiveDelete(fyle);
|
||||
}
|
||||
else {
|
||||
fyle.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix for directory/file structure; default 'remote'.
|
||||
* @return the prefix.
|
||||
*/
|
||||
protected String prefix() {
|
||||
return "remote";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2016 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.stream.app.test.ip;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.cloud.stream.app.test.BinderTestPropertiesInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Generated app test configuration for the IP (TCP) sink.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class IpSinkTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public static BinderTestPropertiesInitializer loadProps(ConfigurableApplicationContext context) {
|
||||
// minimal properties for the context to load
|
||||
Properties properties = new Properties();
|
||||
properties.put("host", "localhost");
|
||||
return new BinderTestPropertiesInitializer(context, properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2016 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.stream.app.test.ip;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.cloud.stream.app.test.BinderTestPropertiesInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Generated app test configuration for IP (TCP, UDP) sources.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class IpSourceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public static BinderTestPropertiesInitializer loadProps(ConfigurableApplicationContext context) {
|
||||
// minimal properties for the context to load
|
||||
Properties properties = new Properties();
|
||||
properties.put("port", 0);
|
||||
return new BinderTestPropertiesInitializer(context, properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
///*
|
||||
// * Copyright 2016 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.stream.app.test.redis;
|
||||
//
|
||||
//import org.springframework.cloud.stream.test.junit.AbstractExternalResourceTestSupport;
|
||||
//import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
//
|
||||
///**
|
||||
// * Porting from https://github.com/spring-cloud/spring-cloud-stream/blob/1.0.x/spring-cloud-stream-test-support-internal
|
||||
// *
|
||||
// * @author Soby Chacko
|
||||
// */
|
||||
//public class RedisTestSupport extends AbstractExternalResourceTestSupport<LettuceConnectionFactory> {
|
||||
//
|
||||
// public RedisTestSupport() {
|
||||
// super("REDIS");
|
||||
// }
|
||||
// @Override
|
||||
// protected void cleanupResource() throws Exception {
|
||||
// resource.destroy();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void obtainResource() throws Exception {
|
||||
// resource = new LettuceConnectionFactory();
|
||||
// resource.afterPropertiesSet();
|
||||
// resource.getConnection().close();
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2016 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.stream.app.test.script;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.cloud.stream.app.test.BinderTestPropertiesInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Test configuration for generated scriptable apps.
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class ScriptableTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public BinderTestPropertiesInitializer loadProps(ConfigurableApplicationContext context) {
|
||||
// minimal properties for the context to load
|
||||
Properties properties = new Properties();
|
||||
properties.put("script", "foo");
|
||||
properties.put("language", "ruby");
|
||||
return new BinderTestPropertiesInitializer(context, properties);
|
||||
}
|
||||
|
||||
}
|
||||
348
applications/apps-core/pom.xml
Normal file
348
applications/apps-core/pom.xml
Normal file
@@ -0,0 +1,348 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-parent</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<name>stream-apps-parent</name>
|
||||
<description>Infrastructure for stream apps</description>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.0.M4</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<stream-apps-core.version>3.0.0.M2</stream-apps-core.version>
|
||||
<spring-boot.version>2.3.0.M4</spring-boot.version>
|
||||
<app-metadata-maven-plugin-version>2.0.1.M1</app-metadata-maven-plugin-version>
|
||||
<scst-app-maven-plugin.version>2.0.0.M2</scst-app-maven-plugin.version>
|
||||
<stream-apps-docs-plugin.version>3.0.0.M1</stream-apps-docs-plugin.version>
|
||||
<spring-cloud-stream.version>3.0.2.RELEASE</spring-cloud-stream.version>
|
||||
<spring-cloud-function-dependencies.version>3.0.2.RELEASE</spring-cloud-function-dependencies.version>
|
||||
<spring-cloud-dependencies.version>Hoxton.SR2</spring-cloud-dependencies.version>
|
||||
<spring-cloud-stream-dependencies.version>Horsham.SR2</spring-cloud-stream-dependencies.version>
|
||||
<java-cfenv-boot.version>2.1.1.RELEASE</java-cfenv-boot.version>
|
||||
<java-functions.version>1.0.0.BUILD-SNAPSHOT</java-functions.version>
|
||||
<prometheus-rsocket.version>0.9.0</prometheus-rsocket.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>common</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-dependencies</artifactId>
|
||||
<version>${spring-cloud-stream-dependencies.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<type>test-jar</type>
|
||||
<classifier>test-binder</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<comments>Copyright 2014-2020 the original author or authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied.
|
||||
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/pivotal/java-functions.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/pivotal/java-functions.git</developerConnection>
|
||||
<url>https://github.com/pivotal/java-functions</url>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>repo.spring.io</id>
|
||||
<name>Spring Release Repository</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>repo.spring.io</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>milestone</id>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>repo.spring.io</id>
|
||||
<name>Spring Milestone Repository</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>central</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>sonatype-nexus-staging</id>
|
||||
<name>Nexus Release Repository</name>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>sonatype-nexus-snapshots</id>
|
||||
<name>Sonatype Nexus Snapshots</name>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/Abstract*.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-app-starter-doc-maven-plugin</artifactId>
|
||||
<version>${stream-apps-docs-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-documentation</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>generate-documentation</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.cloud.stream.app.plugin</groupId>
|
||||
<artifactId>spring-cloud-stream-app-maven-plugin</artifactId>
|
||||
<version>${scst-app-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>app-gen</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>generate-app</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<generatedProjectHome>${basedir}/apps</generatedProjectHome>
|
||||
<javaVersion>1.8</javaVersion>
|
||||
<bootVersion>${spring-boot.version}</bootVersion>
|
||||
|
||||
<binders>
|
||||
<item>kafka</item>
|
||||
<item>rabbit</item>
|
||||
</binders>
|
||||
<boms>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream-dependencies</artifactId>
|
||||
<version>${spring-cloud-stream-dependencies.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-dependencies</artifactId>
|
||||
<version>${spring-cloud-function-dependencies.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud-dependencies.version}</version>
|
||||
</dependency>
|
||||
</boms>
|
||||
<globalDependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-security-common</artifactId>
|
||||
<version>${stream-apps-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-micrometer-common</artifactId>
|
||||
<version>${stream-apps-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-influx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer.prometheus</groupId>
|
||||
<artifactId>prometheus-rsocket-spring</artifactId>
|
||||
<version>${prometheus-rsocket.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-registry-datadog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.pivotal.cfenv</groupId>
|
||||
<artifactId>java-cfenv-boot</artifactId>
|
||||
<version>${java-cfenv-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</globalDependencies>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-libs-release</id>
|
||||
<name>Spring Libs Release</name>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestone-release</id>
|
||||
<name>Spring Milestone Release</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
||||
|
||||
44
applications/apps-metadata/CODE_OF_CONDUCT.adoc
Normal file
44
applications/apps-metadata/CODE_OF_CONDUCT.adoc
Normal file
@@ -0,0 +1,44 @@
|
||||
= Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering an open
|
||||
and welcoming community, we pledge to respect all people who contribute through reporting
|
||||
issues, posting feature requests, updating documentation, submitting pull requests or
|
||||
patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery
|
||||
* Personal attacks
|
||||
* Trolling or insulting/derogatory comments
|
||||
* Public or private harassment
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission
|
||||
* Other unethical or unprofessional conduct
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments,
|
||||
commits, code, wiki edits, issues, and other contributions that are not aligned to this
|
||||
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
|
||||
that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
|
||||
consistently applying these principles to every aspect of managing this project. Project
|
||||
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
|
||||
from the project team.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an
|
||||
individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
||||
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
|
||||
be reviewed and investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
|
||||
with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the
|
||||
https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
|
||||
https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
|
||||
201
applications/apps-metadata/LICENSE
Normal file
201
applications/apps-metadata/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
87
applications/apps-metadata/pom.xml
Normal file
87
applications/apps-metadata/pom.xml
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>apps-metadata</artifactId>
|
||||
<version>Fahrenheit.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<artifactId>stream-apps-parent</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>stream-apps-docs</module>
|
||||
<module>stream-apps-descriptor</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>spring</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</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>
|
||||
<repository>
|
||||
<id>spring-libs-release</id>
|
||||
<name>Spring Libs Release</name>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<id>spring-milestone-release</id>
|
||||
<name>Spring Milestone Release</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</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>
|
||||
</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>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
29
applications/apps-metadata/release-tools/core-tag-next-version.sh
Executable file
29
applications/apps-metadata/release-tools/core-tag-next-version.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The script takes two arguments - currently released version for tagging and next version
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "Please specify the released version and the next version"
|
||||
exit
|
||||
fi
|
||||
|
||||
pushd /tmp
|
||||
|
||||
git clone git@github.com:spring-cloud-stream-app-starters/core.git
|
||||
cd core
|
||||
|
||||
git tag v$1
|
||||
git push origin v$1
|
||||
|
||||
./mvnw versions:set -DnewVersion=$2 -DgenerateBackupPoms=false
|
||||
./mvnw versions:set -DnewVersion=$2 -DgenerateBackupPoms=false -pl :app-starters-core-dependencies
|
||||
|
||||
sed -i '' 's/<app-starters-core-dependencies.version>.*/<app-starters-core-dependencies.version>'"$2"'<\/app-starters-core-dependencies.version>/g' pom.xml
|
||||
|
||||
git commit -am"Next version - $2"
|
||||
git push origin master
|
||||
|
||||
cd ..
|
||||
rm -rf core
|
||||
|
||||
popd
|
||||
38
applications/apps-metadata/release-tools/core-version-check.sh
Executable file
38
applications/apps-metadata/release-tools/core-version-check.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The script takes one argument - release version
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Please specify the release version"
|
||||
exit
|
||||
fi
|
||||
|
||||
pushd /tmp
|
||||
|
||||
git clone git@github.com:spring-cloud-stream-app-starters/core.git
|
||||
cd core
|
||||
|
||||
./mvnw versions:set -DnewVersion=$1 -DgenerateBackupPoms=false -U
|
||||
./mvnw versions:set -DnewVersion=$1 -DgenerateBackupPoms=false -pl :app-starters-core-dependencies -U
|
||||
|
||||
sed -i '' 's/<app-starters-core-dependencies.version>.*/<app-starters-core-dependencies.version>'"$1"'<\/app-starters-core-dependencies.version>/g' pom.xml
|
||||
|
||||
snapshotlines=$(find . -type f -name pom.xml | xargs grep SNAPSHOT | wc -l)
|
||||
rclines=$(find . -type f -name pom.xml | xargs grep .RC | wc -l)
|
||||
milestonelines=$(find . -type f -name pom.xml | xargs grep version | grep .M | wc -l)
|
||||
|
||||
if [ $snapshotlines -eq 0 ] && [ $rclines -eq 0 ] && [$milestonelines -eq 0 ]; then
|
||||
echo "All clear"
|
||||
else
|
||||
echo "Snapshots found."
|
||||
find . -type f -name pom.xml | xargs grep SNAPSHOT
|
||||
echo "SNAPSHOTS: " $snapshotlines
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ..
|
||||
rm -rf core
|
||||
|
||||
popd
|
||||
|
||||
|
||||
39
applications/apps-metadata/release-tools/core-version-upgrade.sh
Executable file
39
applications/apps-metadata/release-tools/core-version-upgrade.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The script takes one argument - release version
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Please specify the release version"
|
||||
exit
|
||||
fi
|
||||
|
||||
pushd /tmp
|
||||
|
||||
git clone git@github.com:spring-cloud-stream-app-starters/core.git
|
||||
cd core
|
||||
|
||||
./mvnw versions:set -DnewVersion=$1 -DgenerateBackupPoms=false -U
|
||||
./mvnw versions:set -DnewVersion=$1 -DgenerateBackupPoms=false -pl :app-starters-core-dependencies -U
|
||||
|
||||
sed -i '' 's/<app-starters-core-dependencies.version>.*/<app-starters-core-dependencies.version>'"$1"'<\/app-starters-core-dependencies.version>/g' pom.xml
|
||||
|
||||
snapshotlines=$(find . -type f -name pom.xml | xargs grep SNAPSHOT | wc -l)
|
||||
rclines=$(find . -type f -name pom.xml | xargs grep .RC | wc -l)
|
||||
milestonelines=$(find . -type f -name pom.xml | xargs grep version | grep .M | wc -l)
|
||||
|
||||
if [ $snapshotlines -eq 0 ] && [ $rclines -eq 0 ] && [$milestonelines -eq 0 ]; then
|
||||
echo "All clear"
|
||||
git commit -am"Update version to $1"
|
||||
git push origin master
|
||||
else
|
||||
echo "Snapshots found."
|
||||
echo "SNAPSHOTS: " $snapshotlines
|
||||
echo "Milestones: " $milestonelines
|
||||
echo "RC: " $rclines
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ..
|
||||
rm -rf core
|
||||
|
||||
popd
|
||||
140
applications/apps-metadata/stream-apps-descriptor/pom.xml
Normal file
140
applications/apps-metadata/stream-apps-descriptor/pom.xml
Normal file
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>stream-apps-release-train</artifactId>
|
||||
<groupId>org.springframework.cloud.stream.app</groupId>
|
||||
<version>Fahrenheit.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>stream-apps-descriptor</artifactId>
|
||||
<name>stream-apps-descriptor</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<cassandra-sink.version>3.0.0.BUILD-SNAPSHOT</cassandra-sink.version>
|
||||
<counter-sink.version>3.0.0.BUILD-SNAPSHOT</counter-sink.version>
|
||||
<jdbc-sink.version>3.0.0.BUILD-SNAPSHOT</jdbc-sink.version>
|
||||
<log-sink.version>3.0.0.BUILD-SNAPSHOT</log-sink.version>
|
||||
<mongodb-sink.version>3.0.0.BUILD-SNAPSHOT</mongodb-sink.version>
|
||||
<rabbit-sink.version>3.0.0.BUILD-SNAPSHOT</rabbit-sink.version>
|
||||
<http-source.version>3.0.0.BUILD-SNAPSHOT</http-source.version>
|
||||
<jdbc-source.version>3.0.0.BUILD-SNAPSHOT</jdbc-source.version>
|
||||
<mongodb-source.version>3.0.0.BUILD-SNAPSHOT</mongodb-source.version>
|
||||
<time-source.version>3.0.0.BUILD-SNAPSHOT</time-source.version>
|
||||
<filter-processor.version>3.0.0.BUILD-SNAPSHOT</filter-processor.version>
|
||||
<splitter-processor.version>3.0.0.BUILD-SNAPSHOT</splitter-processor.version>
|
||||
<transform-processor.version>3.0.0.BUILD-SNAPSHOT</transform-processor.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>META-INF/kafka-apps-maven.properties</include>
|
||||
<include>META-INF/rabbit-apps-maven.properties</include>
|
||||
<include>META-INF/kafka-apps-docker.properties</include>
|
||||
<include>META-INF/rabbit-apps-docker.properties</include>
|
||||
<include>META-INF/kafka-apps-maven-repo-url.properties</include>
|
||||
<include>META-INF/rabbit-apps-maven-repo-url.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.gmaven</groupId>
|
||||
<artifactId>gmaven-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>execute</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<source><![CDATA[
|
||||
pom.properties['repo-spring-io']=
|
||||
"${jdbc-source.version}".contains('BUILD-SNAPSHOT') ? 'repo.spring.io/snapshot' :
|
||||
"${jdbc-source.version}".contains('RELEASE') ? 'repo.spring.io/release' : 'repo.spring.io/milestone';
|
||||
|
||||
pom.properties['cassandra-sink-docker.tag']=
|
||||
"${cassandra-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${cassandra-sink.version}";
|
||||
pom.properties['counter-sink-docker.tag']=
|
||||
"${counter-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${counter-sink.version}";
|
||||
pom.properties['jdbc-sink-docker.tag']=
|
||||
"${jdbc-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${jdbc-sink.version}";
|
||||
pom.properties['log-sink-docker.tag']=
|
||||
"${log-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${log-sink.version}";
|
||||
pom.properties['mongodb-sink-docker.tag']=
|
||||
"${mongodb-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${mongodb-sink.version}";
|
||||
pom.properties['rabbit-sink-docker.tag']=
|
||||
"${rabbit-sink.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${rabbit-sink.version}";
|
||||
pom.properties['http-source-docker.tag']=
|
||||
"${http-source.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${http-source.version}";
|
||||
pom.properties['jdbc-source-docker.tag']=
|
||||
"${jdbc-source.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${jdbc-source.version}";
|
||||
pom.properties['mongodb-source-docker.tag']=
|
||||
"${mongodb-source.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${mongodb-source.version}";
|
||||
pom.properties['time-source-docker.tag']=
|
||||
"${time-source.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${time-source.version}";
|
||||
pom.properties['filter-processor-docker.tag']=
|
||||
"${filter-processor.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${filter-processor.version}";
|
||||
pom.properties['splitter-processor-docker.tag']=
|
||||
"${splitter-processor.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${splitter-processor.version}";
|
||||
pom.properties['transform-processor-docker.tag']=
|
||||
"${transform-processor.version}".contains('BUILD-SNAPSHOT') ? 'latest' : "${transform-processor.version}";
|
||||
]]></source>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-artifacts</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>attach-artifact</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifacts>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/kafka-apps-maven.properties</file>
|
||||
<type>stream-apps-kafka-maven</type>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/kafka-apps-docker.properties</file>
|
||||
<type>stream-apps-kafka-docker</type>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/rabbit-apps-maven.properties</file>
|
||||
<type>stream-apps-rabbit-maven</type>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/rabbit-apps-docker.properties</file>
|
||||
<type>stream-apps-rabbit-docker</type>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/kafka-apps-maven-repo-url.properties</file>
|
||||
<type>kafka-apps-maven-repo-url.properties</type>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<file>target/classes/META-INF/rabbit-apps-maven-repo-url.properties</file>
|
||||
<type>rabbit-apps-maven-repo-url.properties</type>
|
||||
</artifact>
|
||||
</artifacts>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
sink.cassandra=docker:springcloudstream/cassandra-sink-kafka:@cassandra-sink-docker.tag@
|
||||
sink.counter=docker:springcloudstream/counter-sink-kafka:@counter-sink-docker.tag@
|
||||
sink.jdbc=docker:springcloudstream/jdbc-sink-kafka:@jdbc-sink-docker.tag@
|
||||
sink.log=docker:springcloudstream/log-sink-kafka:@log-sink-docker.tag@
|
||||
sink.mongodb=docker:springcloudstream/mongodb-sink-kafka:@mongodb-sink-docker.tag@
|
||||
sink.rabbit=docker:springcloudstream/rabbit-sink-kafka:@rabbit-sink-docker.tag@
|
||||
source.http=docker:springcloudstream/http-source-kafka:@http-source-docker.tag@
|
||||
source.jdbc=docker:springcloudstream/jdbc-source-kafka:@jdbc-source-docker.tag@
|
||||
source.mongodb=docker:springcloudstream/mongodb-source-kafka:@mongodb-source-docker.tag@
|
||||
source.time=docker:springcloudstream/time-source-kafka:@time-source-docker.tag@
|
||||
processor.filter=docker:springcloudstream/filter-processor-kafka:@filter-processor-docker.tag@
|
||||
processor.splitter=docker:springcloudstream/splitter-processor-kafka:@splitter-processor-docker.tag@
|
||||
processor.transform=docker:springcloudstream/transform-processor-kafka:@transform-processor-docker.tag@
|
||||
@@ -0,0 +1,26 @@
|
||||
sink.cassandra=https://@repo-spring-io@/org/springframework/cloud/stream/app/cassandra-sink-kafka/@cassandra-sink.version@/cassandra-sink-kafka-@cassandra-sink.version@.jar
|
||||
sink.cassandra.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/cassandra-sink-kafka/@cassandra-sink.version@/cassandra-sink-kafka-@cassandra-sink.version@-metadata.jar
|
||||
sink.counter=https://@repo-spring-io@/org/springframework/cloud/stream/app/counter-sink-kafka/@counter-sink.version@/counter-sink-kafka-@counter-sink.version@.jar
|
||||
sink.counter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/counter-sink-kafka/@counter-sink.version@/counter-sink-kafka-@counter-sink.version@-metadata.jar
|
||||
sink.jdbc=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-sink-kafka/@jdbc-sink.version@/jdbc-sink-kafka-@jdbc-sink.version@.jar
|
||||
sink.jdbc.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-sink-kafka/@jdbc-sink.version@/jdbc-sink-kafka-@jdbc-sink.version@-metadata.jar
|
||||
sink.log=https://@repo-spring-io@/org/springframework/cloud/stream/app/log-sink-kafka/@log-sink.version@/log-sink-kafka-@log-sink.version@.jar
|
||||
sink.log.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/log-sink-kafka/@log-sink.version@/log-sink-kafka-@log-sink.version@-metadata.jar
|
||||
sink.mongodb=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-sink-kafka/@mongodb-sink.version@/mongodb-sink-kafka-@mongodb-sink.version@.jar
|
||||
sink.mongodb.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-sink-kafka/@mongodb-sink.version@/mongodb-sink-kafka-@mongodb-sink.version@-metadata.jar
|
||||
sink.rabbit=https://@repo-spring-io@/org/springframework/cloud/stream/app/rabbit-sink-kafka/@rabbit-sink.version@/rabbit-sink-kafka-@rabbit-sink.version@.jar
|
||||
sink.rabbit.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/rabbit-sink-kafka/@rabbit-sink.version@/rabbit-sink-kafka-@rabbit-sink.version@-metadata.jar
|
||||
source.http=https://@repo-spring-io@/org/springframework/cloud/stream/app/http-source-kafka/@http-source.version@/http-source-kafka-@http-source.version@.jar
|
||||
source.http.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/http-source-kafka/@http-source.version@/http-source-kafka-@http-source.version@-metadata.jar
|
||||
source.jdbc=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-source-kafka/@jdb-source.version@/jdbc-source-kafka-@jdbc-source.version@.jar
|
||||
source.jdbc.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-source-kafka/@jdb-source.version@/jdbc-source-kafka-@jdbc-source.version@-metadata.jar
|
||||
source.mongodb=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-source-kafka/@mongodb-source.version@/mongodb-source-kafka-@mongodb-source.version@.jar
|
||||
source.mongodb.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-source-kafka/@mongodb-source.version@/mongodb-source-kafka-@mongodb-source.version@-metadata.jar
|
||||
source.time=https://@repo-spring-io@/org/springframework/cloud/stream/app/time-source-kafka/@time-source.version@/time-source-kafka-@time-source.version@.jar
|
||||
source.time.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/time-source-kafka/@time-source.version@/time-source-kafka-@time-source.version@-metadata.jar
|
||||
processor.filter=https://@repo-spring-io@/org/springframework/cloud/stream/app/filter-processor-kafka/@filter-processor.version@/filter-processor-kafka-@filter-processor.version@.jar
|
||||
processor.filter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/filter-processor-kafka/@filter-processor.version@/filter-processor-kafka-@filter-processor.version@-metadata.jar
|
||||
processor.splitter=https://@repo-spring-io@/org/springframework/cloud/stream/app/splitter-processor-kafka/@splitter-processor.version@/splitter-processor-kafka-@splitter-processor.version@.jar
|
||||
processor.splitter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/splitter-processor-kafka/@splitter-processor.version@/splitter-processor-kafka-@splitter-processor.version@-metadata.jar
|
||||
processor.transform=https://@repo-spring-io@/org/springframework/cloud/stream/app/transform-processor-kafka/@transform-processor.version@/transform-processor-kafka-@transform-processor.version@.jar
|
||||
processor.transform.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/transform-processor-kafka/@transform-processor.version@/transform-processor-kafka-@transform-processor.version@-metadata.jar
|
||||
@@ -0,0 +1,27 @@
|
||||
sink.cassandra=maven://org.springframework.cloud.stream.app:cassandra-sink-kafka:@cassandra-sink.version@
|
||||
sink.cassandra.metadata=maven://org.springframework.cloud.stream.app:cassandra-sink-kafka:jar:metadata:@cassandra-sink.version@
|
||||
sink.counter=maven://org.springframework.cloud.stream.app:counter-sink-kafka:@counter-sink.version@
|
||||
sink.counter.metadata=maven://org.springframework.cloud.stream.app:counter-sink-kafka:jar:metadata:@counter-sink.version@
|
||||
sink.jdbc=maven://org.springframework.cloud.stream.app:jdbc-sink-kafka:@jdbc-sink.version@
|
||||
sink.jdbc.metadata=maven://org.springframework.cloud.stream.app:jdbc-sink-kafka:jar:metadata:@jdbc-sink.version@
|
||||
sink.log=maven://org.springframework.cloud.stream.app:log-sink-kafka:@log-sink.version@
|
||||
sink.log.metadata=maven://org.springframework.cloud.stream.app:log-sink-kafka:jar:metadata:@log-sink.version@
|
||||
sink.mongodb=maven://org.springframework.cloud.stream.app:mongodb-sink-kafka:@mongodb-sink.version@
|
||||
sink.mongodb.metadata=maven://org.springframework.cloud.stream.app:mongodb-sink-kafka:jar:metadata:@mongodb-sink.version@
|
||||
sink.rabbit=maven://org.springframework.cloud.stream.app:rabbit-sink-kafka:@rabbit-sink.version@
|
||||
sink.rabbit.metadata=maven://org.springframework.cloud.stream.app:rabbit-sink-kafka:jar:metadata:@rabbit-sink.version@
|
||||
source.http=maven://org.springframework.cloud.stream.app:http-source-kafka:@http-source.version@
|
||||
source.http.metadata=maven://org.springframework.cloud.stream.app:http-source-kafka:jar:metadata:@http-source.version@
|
||||
source.jdbc=maven://org.springframework.cloud.stream.app:jdbc-source-kafka:@jdbc-source.version@
|
||||
source.jdbc.metadata=maven://org.springframework.cloud.stream.app:jdbc-source-kafka:jar:metadata:@jdbc-source.version@
|
||||
source.mongodb=maven://org.springframework.cloud.stream.app:mongodb-source-kafka:@mongodb-source.version@
|
||||
source.mongodb.metadata=maven://org.springframework.cloud.stream.app:mongodb-source-kafka:jar:metadata:@mongodb-source.version@
|
||||
source.time=maven://org.springframework.cloud.stream.app:time-source-kafka:@time-source.version@
|
||||
source.time.metadata=maven://org.springframework.cloud.stream.app:time-source-kafka:jar:metadata:@time-source.version@
|
||||
processor.filter=maven://org.springframework.cloud.stream.app:filter-processor-kafka:@filter-processor.version@
|
||||
processor.filter.metadata=maven://org.springframework.cloud.stream.app:filter-processor-kafka:jar:metadata:@filter-processor.version@
|
||||
processor.splitter=maven://org.springframework.cloud.stream.app:splitter-processor-kafka:@splitter-processor.version@
|
||||
processor.splitter.metadata=maven://org.springframework.cloud.stream.app:splitter-processor-kafka:jar:metadata:@splitter-processor.version@
|
||||
processor.transform=maven://org.springframework.cloud.stream.app:transform-processor-kafka:@transform-processor.version@
|
||||
processor.transform.metadata=maven://org.springframework.cloud.stream.app:transform-processor-kafka:jar:metadata:@transform-processor.version@
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
sink.cassandra=docker:springcloudstream/cassandra-sink-rabbit:@cassandra-sink-docker.tag@
|
||||
sink.counter=docker:springcloudstream/counter-sink-rabbit:@counter-sink-docker.tag@
|
||||
sink.jdbc=docker:springcloudstream/jdbc-sink-rabbit:@jdbc-sink-docker.tag@
|
||||
sink.log=docker:springcloudstream/log-sink-rabbit:@log-sink-docker.tag@
|
||||
sink.mongodb=docker:springcloudstream/mongodb-sink-rabbit:@mongodb-sink-docker.tag@
|
||||
sink.rabbit=docker:springcloudstream/rabbit-sink-rabbit:@rabbit-sink-docker.tag@
|
||||
source.http=docker:springcloudstream/http-source-rabbit:@http-source-docker.tag@
|
||||
source.jdbc=docker:springcloudstream/jdbc-source-rabbit:@jdbc-source-docker.tag@
|
||||
source.mongodb=docker:springcloudstream/mongodb-source-rabbit:@mongodb-source-docker.tag@
|
||||
source.time=docker:springcloudstream/time-source-rabbit:@time-source-docker.tag@
|
||||
processor.filter=docker:springcloudstream/filter-processor-rabbit:@filter-processor-docker.tag@
|
||||
processor.splitter=docker:springcloudstream/splitter-processor-rabbit:@splitter-processor-docker.tag@
|
||||
processor.transform=docker:springcloudstream/transform-processor-rabbit:@transform-processor-docker.tag@
|
||||
@@ -0,0 +1,26 @@
|
||||
sink.cassandra=https://@repo-spring-io@/org/springframework/cloud/stream/app/cassandra-sink-rabbit/@cassandra-sink.version@/cassandra-sink-rabbit-@cassandra-sink.version@.jar
|
||||
sink.cassandra.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/cassandra-sink-rabbit/@cassandra-sink.version@/cassandra-sink-rabbit-@cassandra-sink.version@-metadata.jar
|
||||
sink.counter=https://@repo-spring-io@/org/springframework/cloud/stream/app/counter-sink-rabbit/@counter-sink.version@/counter-sink-rabbit-@counter-sink.version@.jar
|
||||
sink.counter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/counter-sink-rabbit/@counter-sink.version@/counter-sink-rabbit-@counter-sink.version@-metadata.jar
|
||||
sink.jdbc=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-sink-rabbit/@jdbc-sink.version@/jdbc-sink-rabbit-@jdbc-sink.version@.jar
|
||||
sink.jdbc.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-sink-rabbit/@jdbc-sink.version@/jdbc-sink-rabbit-@jdbc-sink.version@-metadata.jar
|
||||
sink.log=https://@repo-spring-io@/org/springframework/cloud/stream/app/log-sink-rabbit/@log-sink.version@/log-sink-rabbit-@log-sink.version@.jar
|
||||
sink.log.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/log-sink-rabbit/@log-sink.version@/log-sink-rabbit-@log-sink.version@-metadata.jar
|
||||
sink.mongodb=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-sink-rabbit/@mongodb-sink.version@/mongodb-sink-rabbit-@mongodb-sink.version@.jar
|
||||
sink.mongodb.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-sink-rabbit/@mongodb-sink.version@/mongodb-sink-rabbit-@mongodb-sink.version@-metadata.jar
|
||||
sink.rabbit=https://@repo-spring-io@/org/springframework/cloud/stream/app/rabbit-sink-rabbit/@rabbit-sink.version@/rabbit-sink-rabbit-@rabbit-sink.version@.jar
|
||||
sink.rabbit.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/rabbit-sink-rabbit/@rabbit-sink.version@/rabbit-sink-rabbit-@rabbit-sink.version@-metadata.jar
|
||||
source.http=https://@repo-spring-io@/org/springframework/cloud/stream/app/http-source-rabbit/@http-source.version@/http-source-rabbit-@http-source.version@.jar
|
||||
source.http.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/http-source-rabbit/@http-source.version@/http-source-rabbit-@http-source.version@-metadata.jar
|
||||
source.jdbc=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-source-rabbit/@jdb-source.version@/jdbc-source-rabbit-@jdbc-source.version@.jar
|
||||
source.jdbc.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/jdbc-source-rabbit/@jdb-source.version@/jdbc-source-rabbit-@jdbc-source.version@-metadata.jar
|
||||
source.mongodb=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-source-rabbit/@mongodb-source.version@/mongodb-source-rabbit-@mongodb-source.version@.jar
|
||||
source.mongodb.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/mongodb-source-rabbit/@mongodb-source.version@/mongodb-source-rabbit-@mongodb-source.version@-metadata.jar
|
||||
source.time=https://@repo-spring-io@/org/springframework/cloud/stream/app/time-source-rabbit/@time-source.version@/time-source-rabbit-@time-source.version@.jar
|
||||
source.time.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/time-source-rabbit/@time-source.version@/time-source-rabbit-@time-source.version@-metadata.jar
|
||||
processor.filter=https://@repo-spring-io@/org/springframework/cloud/stream/app/filter-processor-rabbit/@filter-processor.version@/filter-processor-rabbit-@filter-processor.version@.jar
|
||||
processor.filter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/filter-processor-rabbit/@filter-processor.version@/filter-processor-rabbit-@filter-processor.version@-metadata.jar
|
||||
processor.splitter=https://@repo-spring-io@/org/springframework/cloud/stream/app/splitter-processor-rabbit/@splitter-processor.version@/splitter-processor-rabbit-@splitter-processor.version@.jar
|
||||
processor.splitter.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/splitter-processor-rabbit/@splitter-processor.version@/splitter-processor-rabbit-@splitter-processor.version@-metadata.jar
|
||||
processor.transform=https://@repo-spring-io@/org/springframework/cloud/stream/app/transform-processor-rabbit/@transform-processor.version@/transform-processor-rabbit-@transform-processor.version@.jar
|
||||
processor.transform.metadata=https://@repo-spring-io@/org/springframework/cloud/stream/app/transform-processor-rabbit/@transform-processor.version@/transform-processor-rabbit-@transform-processor.version@-metadata.jar
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user